@decentnetwork/beagle 0.1.43 → 0.1.44
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/desktop/app.js +236 -42
- package/package.json +1 -1
package/dist/desktop/app.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
window.__DK_UI_VERSION="0.1.
|
|
1
|
+
window.__DK_UI_VERSION="0.1.43";
|
|
2
2
|
const ICON_PATHS = {
|
|
3
3
|
// ---- tab bar (the four must feel like one set) ----
|
|
4
4
|
users: '<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/>',
|
|
@@ -1317,7 +1317,7 @@ function PeerRow({ peer, T, active, onClick }) {
|
|
|
1317
1317
|
textOverflow: "ellipsis",
|
|
1318
1318
|
flex: 1,
|
|
1319
1319
|
minWidth: 0
|
|
1320
|
-
} }, peer.lastMsg))), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", flexDirection: "column", alignItems: "flex-end", gap: 4, flexShrink: 0 } }, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 10.5, color: "var(--faint)" } }, peer.lastTime), peer.unread ? /* @__PURE__ */ React.createElement(Unread, { n: peer.unread }) : peer.pending ? /* @__PURE__ */ React.createElement(Icon, { name: "clock", size: 11, stroke: 2.2, color: "var(--warn, #d29922)", title: "friend request pending" }) : /* @__PURE__ */ React.createElement(StatusDot, { online: peer.online })));
|
|
1320
|
+
} }, dkContactPreview(peer.lastMsg) || peer.lastMsg))), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", flexDirection: "column", alignItems: "flex-end", gap: 4, flexShrink: 0 } }, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 10.5, color: "var(--faint)" } }, peer.lastTime), peer.unread ? /* @__PURE__ */ React.createElement(Unread, { n: peer.unread }) : peer.pending ? /* @__PURE__ */ React.createElement(Icon, { name: "clock", size: 11, stroke: 2.2, color: "var(--warn, #d29922)", title: "friend request pending" }) : /* @__PURE__ */ React.createElement(StatusDot, { online: peer.online })));
|
|
1321
1321
|
}
|
|
1322
1322
|
function PeerSidebar({ T, peers, requests, activeId, onSelect, onAct, onAdd, prefillAddr, onPrefillConsumed }) {
|
|
1323
1323
|
const [q, setQ] = React.useState("");
|
|
@@ -1494,6 +1494,108 @@ function dkInlineMarkdown(s) {
|
|
|
1494
1494
|
out += dkHtmlEscape(text.slice(last));
|
|
1495
1495
|
return out;
|
|
1496
1496
|
}
|
|
1497
|
+
function dkParseBeagleContact(raw) {
|
|
1498
|
+
const text = String(raw == null ? "" : raw);
|
|
1499
|
+
const fence = /```\s*beagle-contact\s*\n([\s\S]*?)\n```/i.exec(text);
|
|
1500
|
+
if (fence) {
|
|
1501
|
+
try {
|
|
1502
|
+
const j = JSON.parse(fence[1].trim());
|
|
1503
|
+
if (j && (j.type === "beagle_contact" || j.type === "beagle-contact")) {
|
|
1504
|
+
return {
|
|
1505
|
+
name: j.name || "",
|
|
1506
|
+
ens: j.ens || "",
|
|
1507
|
+
userid: j.userid || j.userId || "",
|
|
1508
|
+
address: j.address || "",
|
|
1509
|
+
avatar: j.avatar || j.avatarUrl || "",
|
|
1510
|
+
description: j.description || ""
|
|
1511
|
+
};
|
|
1512
|
+
}
|
|
1513
|
+
} catch (e) {
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
if (/^\s*BCR1\s+/i.test(text)) {
|
|
1517
|
+
try {
|
|
1518
|
+
const j = JSON.parse(text.replace(/^\s*BCR1\s+/i, "").trim());
|
|
1519
|
+
if (j && j.type === "beagle_contact") {
|
|
1520
|
+
return {
|
|
1521
|
+
name: j.name || "",
|
|
1522
|
+
ens: j.ens || "",
|
|
1523
|
+
userid: j.userid || j.userId || "",
|
|
1524
|
+
address: j.address || "",
|
|
1525
|
+
avatar: j.avatar || "",
|
|
1526
|
+
description: j.description || ""
|
|
1527
|
+
};
|
|
1528
|
+
}
|
|
1529
|
+
} catch (e) {
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
return null;
|
|
1533
|
+
}
|
|
1534
|
+
function dkEncodeBeagleContact(p) {
|
|
1535
|
+
const j = {
|
|
1536
|
+
v: 1,
|
|
1537
|
+
type: "beagle_contact",
|
|
1538
|
+
name: p.name || "",
|
|
1539
|
+
ens: p.ens || "",
|
|
1540
|
+
userid: p.userid || "",
|
|
1541
|
+
address: p.address || "",
|
|
1542
|
+
avatar: p.avatar || "",
|
|
1543
|
+
description: p.description || ""
|
|
1544
|
+
};
|
|
1545
|
+
const head = "**" + (p.name || p.ens || "Beagle user") + "**" + (p.ens ? " \xB7 `" + p.ens + "`" : "");
|
|
1546
|
+
const bio = p.description ? "\n\n" + p.description : "";
|
|
1547
|
+
const link = "\n\n[Open name card](https://app.beagle.chat/#/chat?address=" + encodeURIComponent(p.address || "") + ")";
|
|
1548
|
+
return head + bio + link + "\n\n```beagle-contact\n" + JSON.stringify(j) + "\n```\n";
|
|
1549
|
+
}
|
|
1550
|
+
function dkContactPreview(text) {
|
|
1551
|
+
const s = String(text == null ? "" : text);
|
|
1552
|
+
if (!/beagle-contact|BCR1\s*\{/i.test(s))
|
|
1553
|
+
return null;
|
|
1554
|
+
const mine = /^you:\s*/i.test(s);
|
|
1555
|
+
const card = dkParseBeagleContact(s.replace(/^you:\s*/i, ""));
|
|
1556
|
+
const label = "\u{1F4C7} " + (card && (card.name || card.ens) || "name card");
|
|
1557
|
+
return mine ? "you: " + label : label;
|
|
1558
|
+
}
|
|
1559
|
+
function DkContactCardMsg({ card, mine, T, onNameCard }) {
|
|
1560
|
+
return /* @__PURE__ */ React.createElement(
|
|
1561
|
+
"button",
|
|
1562
|
+
{
|
|
1563
|
+
onClick: () => onNameCard && onNameCard(card),
|
|
1564
|
+
style: {
|
|
1565
|
+
display: "flex",
|
|
1566
|
+
alignItems: "center",
|
|
1567
|
+
gap: 11,
|
|
1568
|
+
padding: "10px 13px",
|
|
1569
|
+
minWidth: 220,
|
|
1570
|
+
maxWidth: 320,
|
|
1571
|
+
borderRadius: 12,
|
|
1572
|
+
border: "1px solid var(--line)",
|
|
1573
|
+
cursor: "pointer",
|
|
1574
|
+
textAlign: "left",
|
|
1575
|
+
background: mine ? "var(--bub-me)" : "var(--bub-them)"
|
|
1576
|
+
}
|
|
1577
|
+
},
|
|
1578
|
+
card.avatar ? /* @__PURE__ */ React.createElement(DkImgAvatar, { url: card.avatar, seed: card.userid || card.address || card.name, size: 40, radius: 10 }) : /* @__PURE__ */ React.createElement(DkEnsAvatar, { userid: card.userid || card.address, fallbackSeed: card.userid || card.address || card.name, size: 40, radius: 10 }),
|
|
1579
|
+
/* @__PURE__ */ React.createElement("span", { style: { minWidth: 0, display: "flex", flexDirection: "column", gap: 2 } }, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--ui)", fontSize: 13.5, fontWeight: 700, color: mine ? "#fff" : "var(--text)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, card.name || card.ens || shortKey(card.userid || card.address, 8, 6)), card.ens && /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 11, color: mine ? "rgba(255,255,255,0.8)" : "var(--accent)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, card.ens), card.description && /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--ui)", fontSize: 11, color: mine ? "rgba(255,255,255,0.7)" : "var(--dim)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, card.description), /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 10, color: mine ? "rgba(255,255,255,0.6)" : "var(--faint)", marginTop: 2, textTransform: "uppercase", letterSpacing: 0.5 } }, T && T.nameCard || "name card", " \u203A"))
|
|
1580
|
+
);
|
|
1581
|
+
}
|
|
1582
|
+
function dkAddressFromHref(href) {
|
|
1583
|
+
try {
|
|
1584
|
+
const u = new URL(String(href || ""), window.location.href);
|
|
1585
|
+
const a = u.searchParams.get("address");
|
|
1586
|
+
if (a && a.trim())
|
|
1587
|
+
return a.trim();
|
|
1588
|
+
const h = u.hash || "";
|
|
1589
|
+
const qi = h.indexOf("?");
|
|
1590
|
+
if (qi >= 0) {
|
|
1591
|
+
const ha = new URLSearchParams(h.slice(qi + 1)).get("address");
|
|
1592
|
+
if (ha && ha.trim())
|
|
1593
|
+
return ha.trim();
|
|
1594
|
+
}
|
|
1595
|
+
} catch (e) {
|
|
1596
|
+
}
|
|
1597
|
+
return "";
|
|
1598
|
+
}
|
|
1497
1599
|
function dkMarkdownHtml(src) {
|
|
1498
1600
|
const lines = String(src == null ? "" : src).replace(/\r\n?/g, "\n").split("\n");
|
|
1499
1601
|
let html = "";
|
|
@@ -1506,6 +1608,7 @@ function dkMarkdownHtml(src) {
|
|
|
1506
1608
|
continue;
|
|
1507
1609
|
}
|
|
1508
1610
|
if (/^```/.test(line)) {
|
|
1611
|
+
const lang = line.replace(/^```/, "").trim().toLowerCase();
|
|
1509
1612
|
i += 1;
|
|
1510
1613
|
const code = [];
|
|
1511
1614
|
while (i < lines.length && !/^```/.test(lines[i])) {
|
|
@@ -1514,6 +1617,8 @@ function dkMarkdownHtml(src) {
|
|
|
1514
1617
|
}
|
|
1515
1618
|
if (i < lines.length)
|
|
1516
1619
|
i += 1;
|
|
1620
|
+
if (lang === "beagle-contact")
|
|
1621
|
+
continue;
|
|
1517
1622
|
html += "<pre><code>" + dkHtmlEscape(code.join("\n")) + "</code></pre>";
|
|
1518
1623
|
continue;
|
|
1519
1624
|
}
|
|
@@ -1709,8 +1814,23 @@ function dkNativeMediaFromText(text) {
|
|
|
1709
1814
|
const name = String(j.fileName || "file") + ext;
|
|
1710
1815
|
return { sender, kind, name, url: "data:" + mime + ";base64," + j.data.replace(/\s+/g, "") };
|
|
1711
1816
|
}
|
|
1712
|
-
function MarkdownText({ text }) {
|
|
1713
|
-
|
|
1817
|
+
function MarkdownText({ text, onNameCard }) {
|
|
1818
|
+
const onClick = (e) => {
|
|
1819
|
+
const a = e.target && e.target.closest && e.target.closest("a");
|
|
1820
|
+
if (!a)
|
|
1821
|
+
return;
|
|
1822
|
+
const addr = dkAddressFromHref(a.getAttribute("href") || "");
|
|
1823
|
+
if (!addr)
|
|
1824
|
+
return;
|
|
1825
|
+
e.preventDefault();
|
|
1826
|
+
e.stopPropagation();
|
|
1827
|
+
const card = dkParseBeagleContact(text) || { address: addr };
|
|
1828
|
+
if (!card.address)
|
|
1829
|
+
card.address = addr;
|
|
1830
|
+
if (onNameCard)
|
|
1831
|
+
onNameCard(card);
|
|
1832
|
+
};
|
|
1833
|
+
return /* @__PURE__ */ React.createElement("div", { className: "dk-md", onClick, dangerouslySetInnerHTML: { __html: dkMarkdownHtml(text) } });
|
|
1714
1834
|
}
|
|
1715
1835
|
function DkChatForm({ form, submitted, peer, onSubmit }) {
|
|
1716
1836
|
const items = form && form.components || [];
|
|
@@ -1882,7 +2002,7 @@ function DkChatForm({ form, submitted, peer, onSubmit }) {
|
|
|
1882
2002
|
return null;
|
|
1883
2003
|
}), err && /* @__PURE__ */ React.createElement("div", { style: { color: "var(--danger, #ff6b6b)", fontSize: 11.5, fontFamily: "var(--mono)" } }, err));
|
|
1884
2004
|
}
|
|
1885
|
-
function Msg({ m, peer, T, onTheater, onDelete, onCancel, onRetry, onReveal, onOpenFile, onCall, onFormSubmit, answered, selMode, selected, onToggleSel, busy, isGroup }) {
|
|
2005
|
+
function Msg({ m, peer, T, onTheater, onDelete, onCancel, onRetry, onReveal, onOpenFile, onCall, onFormSubmit, onNameCard, answered, selMode, selected, onToggleSel, busy, isGroup }) {
|
|
1886
2006
|
const mine = m.from === "me";
|
|
1887
2007
|
const nativeMedia = !m.file ? dkNativeMediaFromText(m.text) : null;
|
|
1888
2008
|
const grp = isGroup && m.dir !== "out" && !m.file && !nativeMedia ? dkGroupParse(m.text) : null;
|
|
@@ -2168,23 +2288,26 @@ function Msg({ m, peer, T, onTheater, onDelete, onCancel, onRetry, onReveal, onO
|
|
|
2168
2288
|
}
|
|
2169
2289
|
}
|
|
2170
2290
|
}
|
|
2171
|
-
) : nativeMedia.kind === "audio" ? /* @__PURE__ */ React.createElement("audio", { controls: true, src: nativeMedia.url, style: { maxWidth: "100%" } }) : /* @__PURE__ */ React.createElement("video", { controls: true, src: nativeMedia.url, style: { maxWidth: "100%", borderRadius: 12, border: "1px solid var(--line)" } })) :
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2291
|
+
) : nativeMedia.kind === "audio" ? /* @__PURE__ */ React.createElement("audio", { controls: true, src: nativeMedia.url, style: { maxWidth: "100%" } }) : /* @__PURE__ */ React.createElement("video", { controls: true, src: nativeMedia.url, style: { maxWidth: "100%", borderRadius: 12, border: "1px solid var(--line)" } })) : (() => {
|
|
2292
|
+
const contact = !read.form ? dkParseBeagleContact(read.text) : null;
|
|
2293
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, null, grp && /* @__PURE__ */ React.createElement(DkGroupSender, { grp, T }), contact && (contact.address || contact.userid) ? /* @__PURE__ */ React.createElement(DkContactCardMsg, { card: contact, mine, T, onNameCard }) : /* @__PURE__ */ React.createElement("div", { style: {
|
|
2294
|
+
padding: "8px 12px",
|
|
2295
|
+
borderRadius: 12,
|
|
2296
|
+
borderBottomRightRadius: mine ? 4 : 12,
|
|
2297
|
+
borderBottomLeftRadius: mine ? 12 : 4,
|
|
2298
|
+
// Incoming messages are tinted by delivery path so the split is
|
|
2299
|
+
// visible: amber = arrived via the express relay (offline), plain =
|
|
2300
|
+
// live online session. Outgoing keep the accent bubble.
|
|
2301
|
+
background: mine ? "var(--bub-me)" : m.via === "offline" ? "rgba(245,158,11,0.16)" : "var(--bub-them)",
|
|
2302
|
+
color: mine ? "#fff" : "var(--text)",
|
|
2303
|
+
border: "1px solid " + (mine ? "transparent" : m.via === "offline" ? "rgba(245,158,11,0.55)" : "var(--line)"),
|
|
2304
|
+
fontFamily: "var(--ui)",
|
|
2305
|
+
fontSize: 13.5,
|
|
2306
|
+
lineHeight: 1.4,
|
|
2307
|
+
letterSpacing: -0.1,
|
|
2308
|
+
wordBreak: "break-word"
|
|
2309
|
+
} }, /* @__PURE__ */ React.createElement(MarkdownText, { text: read.text, onNameCard })));
|
|
2310
|
+
})(), 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), !mine && m.via && /* @__PURE__ */ React.createElement(
|
|
2188
2311
|
"span",
|
|
2189
2312
|
{
|
|
2190
2313
|
title: m.via === "offline" ? "delivered via express relay (offline)" : "delivered over a live session (online)",
|
|
@@ -2213,7 +2336,7 @@ function Msg({ m, peer, T, onTheater, onDelete, onCancel, onRetry, onReveal, onO
|
|
|
2213
2336
|
) : mine && m.status && /* @__PURE__ */ React.createElement(Icon, { name: "checkCheck", size: 12, stroke: 2.2, color: m.status === "read" ? "var(--accent)" : "var(--faint)" })))
|
|
2214
2337
|
);
|
|
2215
2338
|
}
|
|
2216
|
-
function Conversation({ T, peer, lang, thread: threadProp, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread }) {
|
|
2339
|
+
function Conversation({ T, peer, lang, peers, onOpenChat, thread: threadProp, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread }) {
|
|
2217
2340
|
const scrollRef = React.useRef(null);
|
|
2218
2341
|
const isGroup = React.useMemo(() => dkThreadIsGroup(threadProp), [threadProp]);
|
|
2219
2342
|
const followScrollRef = React.useRef(true);
|
|
@@ -2221,6 +2344,7 @@ function Conversation({ T, peer, lang, thread: threadProp, onSend, onSendFile, o
|
|
|
2221
2344
|
const rtcFileRef = React.useRef(null);
|
|
2222
2345
|
const [menu, setMenu] = React.useState(false);
|
|
2223
2346
|
const [pubProfile, setPubProfile] = React.useState(false);
|
|
2347
|
+
const [nameCardProf, setNameCardProf] = React.useState(null);
|
|
2224
2348
|
const [selMode, setSelMode] = React.useState(false);
|
|
2225
2349
|
const [sel, setSel] = React.useState(() => /* @__PURE__ */ new Set());
|
|
2226
2350
|
const [hidden, setHidden] = React.useState(() => /* @__PURE__ */ new Set());
|
|
@@ -2539,6 +2663,7 @@ ${peer.address}`
|
|
|
2539
2663
|
onOpenFile: doOpenFile,
|
|
2540
2664
|
onCall,
|
|
2541
2665
|
onFormSubmit: () => onReloadThread && onReloadThread(),
|
|
2666
|
+
onNameCard: (card) => setNameCardProf(card),
|
|
2542
2667
|
answered,
|
|
2543
2668
|
busy: m.id ? fileBusy[m.id] : void 0,
|
|
2544
2669
|
selMode,
|
|
@@ -2640,7 +2765,24 @@ ${peer.address}`
|
|
|
2640
2765
|
style: { maxWidth: "100%", maxHeight: "100%", objectFit: "contain", borderRadius: 6 }
|
|
2641
2766
|
}
|
|
2642
2767
|
))),
|
|
2643
|
-
pubProfile && /* @__PURE__ */ React.createElement(DkPublicProfile, { T, userid: peer.userId, fallbackName: peer.alias, isFriend: true, onClose: () => setPubProfile(false) })
|
|
2768
|
+
pubProfile && /* @__PURE__ */ React.createElement(DkPublicProfile, { T, userid: peer.userId, fallbackName: peer.alias, isFriend: true, onClose: () => setPubProfile(false) }),
|
|
2769
|
+
nameCardProf && (() => {
|
|
2770
|
+
const cardFriend = (peers || []).find((p) => nameCardProf.userid && p.userId === nameCardProf.userid || nameCardProf.address && p.address === nameCardProf.address);
|
|
2771
|
+
return /* @__PURE__ */ React.createElement(
|
|
2772
|
+
DkPublicProfile,
|
|
2773
|
+
{
|
|
2774
|
+
T,
|
|
2775
|
+
userid: cardFriend && cardFriend.userId || nameCardProf.userid || nameCardProf.address,
|
|
2776
|
+
fallbackName: nameCardProf.name || nameCardProf.ens,
|
|
2777
|
+
isFriend: !!cardFriend,
|
|
2778
|
+
onMessage: cardFriend && onOpenChat ? () => {
|
|
2779
|
+
onOpenChat(cardFriend.id);
|
|
2780
|
+
setNameCardProf(null);
|
|
2781
|
+
} : void 0,
|
|
2782
|
+
onClose: () => setNameCardProf(null)
|
|
2783
|
+
}
|
|
2784
|
+
);
|
|
2785
|
+
})()
|
|
2644
2786
|
);
|
|
2645
2787
|
}
|
|
2646
2788
|
function MenuItem({ icon, label, onClick, danger }) {
|
|
@@ -2666,7 +2808,7 @@ function ChatEmpty({ T }) {
|
|
|
2666
2808
|
}
|
|
2667
2809
|
function ChatTab({ T, lang, peers, requests, activeId, thread, onSelect, onAct, onAdd, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread, prefillAddr, onPrefillConsumed }) {
|
|
2668
2810
|
const peer = peers.find((p) => p.id === activeId);
|
|
2669
|
-
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 }), peer ? /* @__PURE__ */ React.createElement(Conversation, { T, peer, lang, thread, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread }) : /* @__PURE__ */ React.createElement(ChatEmpty, { T }));
|
|
2811
|
+
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 }), peer ? /* @__PURE__ */ React.createElement(Conversation, { T, peer, lang, peers, onOpenChat: onSelect, thread, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread }) : /* @__PURE__ */ React.createElement(ChatEmpty, { T }));
|
|
2670
2812
|
}
|
|
2671
2813
|
Object.assign(window, { ChatTab });
|
|
2672
2814
|
function StatTile({ label, value, sub, tone }) {
|
|
@@ -3008,9 +3150,34 @@ function DkPunkPicker({ T, onPick, onClose }) {
|
|
|
3008
3150
|
/* @__PURE__ */ React.createElement("img", { src: p.image, alt: `punk #${p.id}`, style: { width: "100%", height: "100%", imageRendering: "pixelated", display: "block" } })
|
|
3009
3151
|
))))));
|
|
3010
3152
|
}
|
|
3011
|
-
function DkPublicProfile({ T, userid, fallbackName, onClose, isFriend, isMe }) {
|
|
3153
|
+
function DkPublicProfile({ T, userid, fallbackName, onClose, isFriend, isMe, onMessage }) {
|
|
3012
3154
|
const [st, setSt] = React.useState({ loading: true });
|
|
3013
3155
|
const [addState, setAddState] = React.useState(null);
|
|
3156
|
+
const [recOpen, setRecOpen] = React.useState(false);
|
|
3157
|
+
const [recPeers, setRecPeers] = React.useState(null);
|
|
3158
|
+
const [recState, setRecState] = React.useState({});
|
|
3159
|
+
const openRecommend = () => {
|
|
3160
|
+
setRecOpen(true);
|
|
3161
|
+
if (recPeers)
|
|
3162
|
+
return;
|
|
3163
|
+
fetch("/api/desktop").then((r) => r.json()).then((d) => {
|
|
3164
|
+
const list = (d && d.peers || []).filter((p) => p.userId && p.userId !== userid && !p.pending);
|
|
3165
|
+
setRecPeers(list);
|
|
3166
|
+
}).catch(() => setRecPeers([]));
|
|
3167
|
+
};
|
|
3168
|
+
const recommendTo = (p) => {
|
|
3169
|
+
const prof2 = st.prof || {};
|
|
3170
|
+
const text = dkEncodeBeagleContact({
|
|
3171
|
+
name: prof2.displayName || fallbackName || "",
|
|
3172
|
+
ens: prof2.ens || "",
|
|
3173
|
+
userid,
|
|
3174
|
+
address: prof2.address || "",
|
|
3175
|
+
avatar: prof2.avatarUrl || "",
|
|
3176
|
+
description: prof2.description || ""
|
|
3177
|
+
});
|
|
3178
|
+
setRecState((s) => ({ ...s, [p.id]: "busy" }));
|
|
3179
|
+
fetch("/api/chat-send", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ userid: p.userId, text }) }).then((r) => r.json()).then((r) => setRecState((s) => ({ ...s, [p.id]: r.ok !== false ? "sent" : r.error || "failed" }))).catch((e) => setRecState((s) => ({ ...s, [p.id]: String(e && e.message || e) })));
|
|
3180
|
+
};
|
|
3014
3181
|
const addFriend = (address) => {
|
|
3015
3182
|
setAddState("busy");
|
|
3016
3183
|
fetch("/api/add", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ address }) }).then((r) => r.json()).then((r) => setAddState(r.ok !== false ? "sent" : r.error || T.addFailed || "failed")).catch((e) => setAddState(String(e && e.message || e)));
|
|
@@ -3050,25 +3217,42 @@ function DkPublicProfile({ T, userid, fallbackName, onClose, isFriend, isMe }) {
|
|
|
3050
3217
|
)))), (() => {
|
|
3051
3218
|
const kvRow = (label, value, head = 8) => /* @__PURE__ */ React.createElement("div", { style: rowS, key: label }, /* @__PURE__ */ React.createElement("span", { style: lbl }, label), /* @__PURE__ */ React.createElement("span", { title: value, style: { flex: 1, minWidth: 0, fontFamily: "var(--mono)", fontSize: 11.5, color: "var(--dim)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, shortKey(value, head, 6)), /* @__PURE__ */ React.createElement(CopyBtn, { value, copiedText: T.copied, copyFailedText: T.copyFailed, copyTitle: T.copy }));
|
|
3052
3219
|
return /* @__PURE__ */ React.createElement(React.Fragment, null, prof.eth && kvRow("ethereum", prof.eth), prof.sol && kvRow("solana", prof.sol), kvRow(T.userId || "user id", userid, 10), prof.address && kvRow(T.addr || "address", prof.address));
|
|
3053
|
-
})(),
|
|
3220
|
+
})(), (() => {
|
|
3221
|
+
const cta = { padding: "7px 16px", borderRadius: 9, cursor: "pointer", fontFamily: "var(--ui)", fontSize: 12.5 };
|
|
3222
|
+
return /* @__PURE__ */ React.createElement("div", { style: { ...rowS, justifyContent: "flex-end", gap: 8 } }, !isMe && prof.address && /* @__PURE__ */ React.createElement(
|
|
3223
|
+
"button",
|
|
3224
|
+
{
|
|
3225
|
+
onClick: openRecommend,
|
|
3226
|
+
style: { ...cta, border: "1px solid var(--line)", background: "transparent", color: "var(--text)" }
|
|
3227
|
+
},
|
|
3228
|
+
T.pubRecommend || "Recommend"
|
|
3229
|
+
), isFriend && onMessage && /* @__PURE__ */ React.createElement(
|
|
3230
|
+
"button",
|
|
3231
|
+
{
|
|
3232
|
+
onClick: onMessage,
|
|
3233
|
+
style: { ...cta, border: "none", background: "var(--accent)", color: "#fff" }
|
|
3234
|
+
},
|
|
3235
|
+
T.pubMessage || "Message"
|
|
3236
|
+
), !isMe && !isFriend && prof.address && (addState === "sent" ? /* @__PURE__ */ React.createElement(Tag, { tone: "accent" }, T.dirSent || "requested") : addState && addState !== "busy" ? /* @__PURE__ */ React.createElement("span", { title: addState, style: { fontFamily: "var(--ui)", fontSize: 11.5, color: "var(--warn, #f59e0b)" } }, T.addFailed || "failed") : /* @__PURE__ */ React.createElement(
|
|
3237
|
+
"button",
|
|
3238
|
+
{
|
|
3239
|
+
onClick: () => addFriend(prof.address),
|
|
3240
|
+
disabled: addState === "busy",
|
|
3241
|
+
style: { ...cta, border: "none", background: "var(--accent)", color: "#fff", opacity: addState === "busy" ? 0.6 : 1 }
|
|
3242
|
+
},
|
|
3243
|
+
addState === "busy" ? "\u2026" : T.dirAdd || "Add"
|
|
3244
|
+
)));
|
|
3245
|
+
})(), recOpen && /* @__PURE__ */ React.createElement("div", { style: { borderTop: "1px solid var(--line)", paddingTop: 10, display: "flex", flexDirection: "column", gap: 4, maxHeight: 200, overflowY: "auto" } }, /* @__PURE__ */ React.createElement("span", { style: { ...lbl, width: "auto", paddingBottom: 4 } }, T.pubRecTitle || "send this card to\u2026"), !recPeers ? /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--ui)", fontSize: 12, color: "var(--faint)" } }, T.dirLoading || "loading\u2026") : recPeers.length === 0 ? /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--ui)", fontSize: 12, color: "var(--faint)" } }, T.dirEmpty || "nobody") : recPeers.map((p) => /* @__PURE__ */ React.createElement(
|
|
3054
3246
|
"button",
|
|
3055
3247
|
{
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
style: {
|
|
3059
|
-
padding: "7px 16px",
|
|
3060
|
-
borderRadius: 9,
|
|
3061
|
-
border: "none",
|
|
3062
|
-
cursor: addState === "busy" ? "default" : "pointer",
|
|
3063
|
-
background: "var(--accent)",
|
|
3064
|
-
color: "#fff",
|
|
3065
|
-
fontFamily: "var(--ui)",
|
|
3066
|
-
fontSize: 12.5,
|
|
3067
|
-
opacity: addState === "busy" ? 0.6 : 1
|
|
3068
|
-
}
|
|
3248
|
+
key: p.id,
|
|
3249
|
+
onClick: () => !recState[p.id] && recommendTo(p),
|
|
3250
|
+
style: { display: "flex", alignItems: "center", gap: 9, padding: "6px 8px", borderRadius: 9, border: "none", background: "transparent", cursor: recState[p.id] ? "default" : "pointer", textAlign: "left" }
|
|
3069
3251
|
},
|
|
3070
|
-
|
|
3071
|
-
|
|
3252
|
+
/* @__PURE__ */ React.createElement(DkAvatar, { peer: p, size: 26, radius: 7, dot: false }),
|
|
3253
|
+
/* @__PURE__ */ React.createElement("span", { style: { flex: 1, minWidth: 0, fontFamily: "var(--ui)", fontSize: 12.5, color: "var(--text)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, p.alias || shortKey(p.userId, 8, 6)),
|
|
3254
|
+
recState[p.id] === "sent" ? /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--ui)", fontSize: 11.5, color: "var(--online)" } }, T.pubRecSent || "sent \u2713") : recState[p.id] === "busy" ? /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--ui)", fontSize: 11.5, color: "var(--faint)" } }, "\u2026") : recState[p.id] ? /* @__PURE__ */ React.createElement("span", { title: recState[p.id], style: { fontFamily: "var(--ui)", fontSize: 11.5, color: "var(--warn, #f59e0b)" } }, T.addFailed || "failed") : /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--ui)", fontSize: 11.5, color: "var(--accent)" } }, T.send || "Send")
|
|
3255
|
+
))))));
|
|
3072
3256
|
}
|
|
3073
3257
|
function DkEnsCard({ T, me, onRecord }) {
|
|
3074
3258
|
const [st, setSt] = React.useState({ loading: true });
|
|
@@ -4417,6 +4601,11 @@ const STR = {
|
|
|
4417
4601
|
linkPlaceholder: "username or profile URL",
|
|
4418
4602
|
save: "save",
|
|
4419
4603
|
addr: "address",
|
|
4604
|
+
nameCard: "name card",
|
|
4605
|
+
pubMessage: "Message",
|
|
4606
|
+
pubRecommend: "Recommend",
|
|
4607
|
+
pubRecTitle: "send this card to\u2026",
|
|
4608
|
+
pubRecSent: "sent \u2713",
|
|
4420
4609
|
updTitle: "Update available",
|
|
4421
4610
|
updLater: "Later",
|
|
4422
4611
|
updNow: "Update now",
|
|
@@ -4581,6 +4770,11 @@ const STR = {
|
|
|
4581
4770
|
linkPlaceholder: "\u7528\u6237\u540D\u6216\u4E3B\u9875\u94FE\u63A5",
|
|
4582
4771
|
save: "\u4FDD\u5B58",
|
|
4583
4772
|
addr: "\u5730\u5740",
|
|
4773
|
+
nameCard: "\u540D\u7247",
|
|
4774
|
+
pubMessage: "\u53D1\u6D88\u606F",
|
|
4775
|
+
pubRecommend: "\u63A8\u8350",
|
|
4776
|
+
pubRecTitle: "\u628A\u540D\u7247\u53D1\u9001\u7ED9\u2026",
|
|
4777
|
+
pubRecSent: "\u5DF2\u53D1\u9001 \u2713",
|
|
4584
4778
|
updTitle: "\u53D1\u73B0\u65B0\u7248\u672C",
|
|
4585
4779
|
updLater: "\u7A0D\u540E",
|
|
4586
4780
|
updNow: "\u7ACB\u5373\u66F4\u65B0",
|
package/package.json
CHANGED