@decentnetwork/beagle-ui 0.2.12 → 0.2.14
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 +340 -89
- 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.14";
|
|
2
2
|
// src/icons.jsx
|
|
3
3
|
var ICON_PATHS = {
|
|
4
4
|
// ---- tab bar (the four must feel like one set) ----
|
|
@@ -110,6 +110,7 @@ Object.assign(ICON_PATHS, {
|
|
|
110
110
|
unlink: '<path d="M9 15 5.5 18.5a3.5 3.5 0 0 1-5-5L4 10"/><path d="m15 9 3.5-3.5a3.5 3.5 0 0 1 5 5L20 14"/><path d="M8 8 4 4M16 16l4 4M3 11h2M11 3v2"/>',
|
|
111
111
|
zap: '<path d="M13 2 4 13h7l-1 9 9-11h-7l1-9Z"/>',
|
|
112
112
|
clock: '<circle cx="12" cy="12" r="9"/><path d="M12 7v5l3.5 2"/>',
|
|
113
|
+
bellOff: '<path d="M8.7 3.4A6 6 0 0 1 18 8.5c0 3 .5 5 1.1 6.3M6.1 6.1A6 6 0 0 0 6 8.5c0 6.5-2.5 8-2.5 8H16"/><path d="M13.7 20.5a2 2 0 0 1-3.4 0"/><path d="m3 3 18 18"/>',
|
|
113
114
|
plug: '<path d="M9 2v6M15 2v6"/><path d="M7 8h10v3a5 5 0 0 1-10 0V8Z"/><path d="M12 16v6"/>'
|
|
114
115
|
});
|
|
115
116
|
|
|
@@ -499,7 +500,7 @@ function RequestsBlock({ T, requests, onAct }) {
|
|
|
499
500
|
paddingLeft: 8
|
|
500
501
|
} }, r.descr), failed[r.id] && /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 11, color: "var(--danger)" } }, failed[r.id]), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", gap: 6 } }, /* @__PURE__ */ React.createElement(Btn, { tone: "ok", icon: "check", size: "sm", onClick: () => act(r, "accept"), style: { flex: 1 } }, T.accept), /* @__PURE__ */ React.createElement(Btn, { tone: "danger", icon: "x", size: "sm", onClick: () => act(r, "reject"), style: { flex: 1 } }, T.reject))))));
|
|
501
502
|
}
|
|
502
|
-
function PeerRow({ peer, T, active, onClick }) {
|
|
503
|
+
function PeerRow({ peer, T, active, onClick, muted }) {
|
|
503
504
|
const name = peer.alias || peer.userId;
|
|
504
505
|
return /* @__PURE__ */ React.createElement("button", { onClick, style: {
|
|
505
506
|
display: "flex",
|
|
@@ -521,7 +522,7 @@ function PeerRow({ peer, T, active, onClick }) {
|
|
|
521
522
|
whiteSpace: "nowrap",
|
|
522
523
|
overflow: "hidden",
|
|
523
524
|
textOverflow: "ellipsis"
|
|
524
|
-
} }, peer.alias || shortKey(peer.userId, 8, 5)), peer.agent && /* @__PURE__ */ React.createElement(Icon, { name: "bot", size: 13, color: "var(--faint)", stroke: 2 }), peer.wire === "64" && /* @__PURE__ */ React.createElement(Tag, { tone: "off", style: { height: 15, fontSize: 9.5, padding: "0 4px" } }, "w64")), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 6, marginTop: 2 } }, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 11, color: "var(--faint)" } }, peer.ip), /* @__PURE__ */ React.createElement("span", { style: { color: "var(--line)", fontSize: 11 } }, "\xB7"), /* @__PURE__ */ React.createElement("span", { style: {
|
|
525
|
+
} }, peer.alias || shortKey(peer.userId, 8, 5)), peer.agent && /* @__PURE__ */ React.createElement(Icon, { name: "bot", size: 13, color: "var(--faint)", stroke: 2 }), muted && /* @__PURE__ */ React.createElement(Icon, { name: "bellOff", size: 12, color: "var(--faint)", stroke: 2, title: T && T.mutedTag || "sound muted" }), peer.wire === "64" && /* @__PURE__ */ React.createElement(Tag, { tone: "off", style: { height: 15, fontSize: 9.5, padding: "0 4px" } }, "w64")), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 6, marginTop: 2 } }, peer.ip ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 11, color: "var(--faint)" } }, peer.ip), /* @__PURE__ */ React.createElement("span", { style: { color: "var(--line)", fontSize: 11 } }, "\xB7")) : null, /* @__PURE__ */ React.createElement("span", { style: {
|
|
525
526
|
fontSize: 11,
|
|
526
527
|
color: "var(--dim)",
|
|
527
528
|
whiteSpace: "nowrap",
|
|
@@ -529,12 +530,12 @@ function PeerRow({ peer, T, active, onClick }) {
|
|
|
529
530
|
textOverflow: "ellipsis",
|
|
530
531
|
flex: 1,
|
|
531
532
|
minWidth: 0
|
|
532
|
-
} },
|
|
533
|
+
} }, dkRowPreview(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 })));
|
|
533
534
|
}
|
|
534
535
|
function dkActivityTs(p) {
|
|
535
536
|
return Math.max(p.lastTs || 0, p.acceptedAt || 0, p.requestedAt || 0);
|
|
536
537
|
}
|
|
537
|
-
function PeerSidebar({ T, peers, requests, activeId, onSelect, onAct, onAdd, prefillAddr, onPrefillConsumed }) {
|
|
538
|
+
function PeerSidebar({ T, peers, requests, activeId, onSelect, onAct, onAdd, prefillAddr, onPrefillConsumed, muted, wide }) {
|
|
538
539
|
const [q, setQ] = React.useState("");
|
|
539
540
|
const [addr, setAddr] = React.useState("");
|
|
540
541
|
const [addState, setAddState] = React.useState(null);
|
|
@@ -583,7 +584,7 @@ function PeerSidebar({ T, peers, requests, activeId, onSelect, onAct, onAdd, pre
|
|
|
583
584
|
return String(a.alias || a.userId || "").localeCompare(String(b.alias || b.userId || ""));
|
|
584
585
|
});
|
|
585
586
|
const online = peers.filter((p) => p.online).length;
|
|
586
|
-
return /* @__PURE__ */ React.createElement("div", { style: { width: 320, flexShrink: 0, borderRight: "1px solid var(--line)", display: "flex", flexDirection: "column", background: "var(--panel)" } }, /* @__PURE__ */ React.createElement("div", { style: { padding: "12px 12px 10px", borderBottom: "1px solid var(--line)", display: "flex", flexDirection: "column", gap: 8 } }, fromDeepLink && /* @__PURE__ */ React.createElement("div", { style: {
|
|
587
|
+
return /* @__PURE__ */ React.createElement("div", { style: wide ? { flex: 1, minWidth: 0, minHeight: 0, display: "flex", flexDirection: "column", background: "var(--panel)" } : { width: 320, flexShrink: 0, borderRight: "1px solid var(--line)", display: "flex", flexDirection: "column", background: "var(--panel)" } }, /* @__PURE__ */ React.createElement("div", { style: { padding: "12px 12px 10px", borderBottom: "1px solid var(--line)", display: "flex", flexDirection: "column", gap: 8 } }, fromDeepLink && /* @__PURE__ */ React.createElement("div", { style: {
|
|
587
588
|
fontFamily: "var(--ui)",
|
|
588
589
|
fontSize: 12,
|
|
589
590
|
lineHeight: 1.4,
|
|
@@ -616,7 +617,7 @@ function PeerSidebar({ T, peers, requests, activeId, onSelect, onAct, onAdd, pre
|
|
|
616
617
|
fontSize: 11.5,
|
|
617
618
|
lineHeight: 1.35,
|
|
618
619
|
color: addState.kind === "err" ? "var(--bad, #e5484d)" : addState.kind === "ok" ? "var(--good, #46a758)" : "var(--faint)"
|
|
619
|
-
} }, addState.msg), /* @__PURE__ */ React.createElement("div", { style: { position: "relative", display: "flex", alignItems: "center" } }, /* @__PURE__ */ React.createElement(Icon, { name: "search", size: 15, color: "var(--faint)", stroke: 2, style: { position: "absolute", left: 9 } }), /* @__PURE__ */ React.createElement("input", { value: q, onChange: (e) => setQ(e.target.value), placeholder: T.search, style: { ...inputStyle, paddingLeft: 30 } }))), /* @__PURE__ */ React.createElement("div", { style: { flex: 1, overflow: "auto", padding: "10px 8px 16px" } }, /* @__PURE__ */ React.createElement(RequestsBlock, { T, requests, onAct }), /* @__PURE__ */ React.createElement(Section, { label: T.peers, count: `${online}/${peers.length}`, style: { margin: "6px 4px 8px" } }), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 2 } }, filtered.map((p) => /* @__PURE__ */ React.createElement(PeerRow, { key: p.id, peer: p, T, active: p.id === activeId, onClick: () => onSelect(p.id) })), !filtered.length && /* @__PURE__ */ React.createElement("div", { style: { padding: 16, textAlign: "center", fontFamily: "var(--mono)", fontSize: 12, color: "var(--faint)" } }, "no matches"))));
|
|
620
|
+
} }, addState.msg), /* @__PURE__ */ React.createElement("div", { style: { position: "relative", display: "flex", alignItems: "center" } }, /* @__PURE__ */ React.createElement(Icon, { name: "search", size: 15, color: "var(--faint)", stroke: 2, style: { position: "absolute", left: 9 } }), /* @__PURE__ */ React.createElement("input", { value: q, onChange: (e) => setQ(e.target.value), placeholder: T.search, style: { ...inputStyle, paddingLeft: 30 } }))), /* @__PURE__ */ React.createElement("div", { style: { flex: 1, overflow: "auto", padding: "10px 8px 16px" } }, /* @__PURE__ */ React.createElement(RequestsBlock, { T, requests, onAct }), /* @__PURE__ */ React.createElement(Section, { label: T.peers, count: `${online}/${peers.length}`, style: { margin: "6px 4px 8px" } }), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 2 } }, filtered.map((p) => /* @__PURE__ */ React.createElement(PeerRow, { key: p.id, peer: p, T, active: p.id === activeId, onClick: () => onSelect(p.id), muted: !!muted && muted.includes(p.id) })), !filtered.length && /* @__PURE__ */ React.createElement("div", { style: { padding: 16, textAlign: "center", fontFamily: "var(--mono)", fontSize: 12, color: "var(--faint)" } }, "no matches"))));
|
|
620
621
|
}
|
|
621
622
|
var inputStyle = {
|
|
622
623
|
flex: 1,
|
|
@@ -776,6 +777,17 @@ function dkEncodeBeagleContact(p) {
|
|
|
776
777
|
const link = "\n\n[Open name card](https://app.beagle.chat/#/chat?address=" + encodeURIComponent(p.address || "") + ")";
|
|
777
778
|
return head + bio + link + "\n\n```beagle-contact\n" + JSON.stringify(j) + "\n```\n";
|
|
778
779
|
}
|
|
780
|
+
function dkRowPreview(text) {
|
|
781
|
+
const card = dkContactPreview(text);
|
|
782
|
+
if (card)
|
|
783
|
+
return card;
|
|
784
|
+
const s = String(text == null ? "" : text);
|
|
785
|
+
const mine = /^you:\s*/i.test(s);
|
|
786
|
+
const cgp = dkParseCGP1(s.replace(/^you:\s*/i, ""));
|
|
787
|
+
if (cgp)
|
|
788
|
+
return (mine ? "you: " : "") + cgp.sender + ": " + cgp.body.replace(/\s+/g, " ");
|
|
789
|
+
return s;
|
|
790
|
+
}
|
|
779
791
|
function dkContactPreview(text) {
|
|
780
792
|
const s = String(text == null ? "" : text);
|
|
781
793
|
if (!/beagle-contact|BCR1\s*\{/i.test(s))
|
|
@@ -1418,6 +1430,8 @@ function Msg({ m, peer, T, onTheater, onDelete, onCancel, onRetry, onRetryMsg, o
|
|
|
1418
1430
|
background: mine ? "var(--bub-me)" : "var(--bub-them)",
|
|
1419
1431
|
border: "1px solid " + (mine ? "transparent" : "var(--line)"),
|
|
1420
1432
|
minWidth: 200,
|
|
1433
|
+
maxWidth: "100%",
|
|
1434
|
+
boxSizing: "border-box",
|
|
1421
1435
|
textDecoration: "none",
|
|
1422
1436
|
cursor: mine ? "default" : "pointer"
|
|
1423
1437
|
};
|
|
@@ -1601,7 +1615,7 @@ function Msg({ m, peer, T, onTheater, onDelete, onCancel, onRetry, onRetryMsg, o
|
|
|
1601
1615
|
) : 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)" })))
|
|
1602
1616
|
);
|
|
1603
1617
|
}
|
|
1604
|
-
function Conversation({ T, peer, lang, peers, onOpenChat, thread: threadProp, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread }) {
|
|
1618
|
+
function Conversation({ T, peer, lang, peers, onOpenChat, thread: threadProp, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread, muted, onToggleMute, phone, onBack }) {
|
|
1605
1619
|
const scrollRef = React.useRef(null);
|
|
1606
1620
|
const isGroup = React.useMemo(() => dkThreadIsGroup(threadProp), [threadProp]);
|
|
1607
1621
|
const followScrollRef = React.useRef(true);
|
|
@@ -1874,7 +1888,16 @@ function Conversation({ T, peer, lang, peers, onOpenChat, thread: threadProp, on
|
|
|
1874
1888
|
onDrop
|
|
1875
1889
|
},
|
|
1876
1890
|
dragOver && /* @__PURE__ */ React.createElement("div", { style: { position: "absolute", inset: 0, zIndex: 30, background: "rgba(91,140,255,0.10)", border: "2px dashed var(--accent)", borderRadius: 12, display: "flex", alignItems: "center", justifyContent: "center", pointerEvents: "none", fontFamily: "var(--mono)", fontSize: 14, color: "var(--accent)" } }, lang === "zh" ? "\u677E\u5F00\u53D1\u9001\u6587\u4EF6" : "Drop to send file"),
|
|
1877
|
-
/* @__PURE__ */ React.createElement("div", { style: { height:
|
|
1891
|
+
/* @__PURE__ */ React.createElement("div", { style: { height: phone ? 56 : 60, boxSizing: "content-box", paddingTop: phone ? "env(safe-area-inset-top, 0px)" : 0, flexShrink: 0, borderBottom: "1px solid var(--line)", display: "flex", alignItems: "center", gap: phone ? 8 : 12, paddingLeft: phone ? 4 : 16, paddingRight: phone ? 8 : 16, background: "var(--panel)" } }, phone && onBack && /* @__PURE__ */ React.createElement(
|
|
1892
|
+
"button",
|
|
1893
|
+
{
|
|
1894
|
+
onClick: onBack,
|
|
1895
|
+
title: T && T.back || "Back",
|
|
1896
|
+
"aria-label": T && T.back || "Back",
|
|
1897
|
+
style: { width: 40, height: 44, flexShrink: 0, border: "none", background: "transparent", cursor: "pointer", display: "flex", alignItems: "center", justifyContent: "center", padding: 0 }
|
|
1898
|
+
},
|
|
1899
|
+
/* @__PURE__ */ React.createElement(Icon, { name: "chevronLeft", size: 26, stroke: 2.2, color: "var(--accent)" })
|
|
1900
|
+
), /* @__PURE__ */ React.createElement("div", { onClick: () => setPubProfile(true), title: T && T.pubOpen || "View public profile", style: { cursor: "pointer", flexShrink: 0 } }, /* @__PURE__ */ React.createElement(DkAvatar, { peer, size: 34, radius: 8 })), /* @__PURE__ */ React.createElement("div", { style: { minWidth: 0, flex: phone ? 1 : void 0 } }, /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 8, minWidth: 0 } }, /* @__PURE__ */ React.createElement("span", { onClick: () => setPubProfile(true), style: { fontFamily: peer.alias ? "var(--ui)" : "var(--mono)", fontSize: 15, fontWeight: 700, color: "var(--text)", cursor: "pointer", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis", minWidth: 0 } }, peer.alias || shortKey(peer.userId, phone ? 6 : 10, phone ? 4 : 6)), peer.agent && !phone && /* @__PURE__ */ React.createElement(Tag, { tone: "accent" }, "agent"), isGroup && !phone && /* @__PURE__ */ React.createElement(Tag, null, T && T.groupTag || "group"), !phone && /* @__PURE__ */ React.createElement(RouteTag, { peer })), phone ? /* @__PURE__ */ React.createElement("div", { style: { marginTop: 2, display: "flex" } }, /* @__PURE__ */ React.createElement(RouteTag, { peer })) : /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 8, marginTop: 2 } }, /* @__PURE__ */ React.createElement(Mono, { size: 11.5, dim: true, copy: peer.userId, title: peer.userId }, shortKey(peer.userId, 10, 6)), /* @__PURE__ */ React.createElement("span", { style: { color: "var(--line)" } }, "\xB7"), /* @__PURE__ */ React.createElement("button", { onClick: () => onOpenNet(peer), title: T && T.openNet || "Network view", style: { background: "none", border: "none", cursor: "pointer", padding: 0, color: "var(--accent)", display: "inline-flex", alignItems: "center" } }, /* @__PURE__ */ React.createElement(Icon, { name: "network", size: 12, stroke: 2 })), /* @__PURE__ */ React.createElement(Mono, { size: 11.5, dim: true, copy: peer.ip, title: peer.ip }, peer.ip), peer.address && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", { style: { color: "var(--line)" } }, "\xB7"), /* @__PURE__ */ React.createElement(
|
|
1878
1901
|
Mono,
|
|
1879
1902
|
{
|
|
1880
1903
|
size: 11.5,
|
|
@@ -1885,7 +1908,14 @@ function Conversation({ T, peer, lang, peers, onOpenChat, thread: threadProp, on
|
|
|
1885
1908
|
${peer.address}`
|
|
1886
1909
|
},
|
|
1887
1910
|
shortKey(peer.address, 8, 6)
|
|
1888
|
-
)))), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }),
|
|
1911
|
+
)))), !phone && /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }), onToggleMute && !phone && /* @__PURE__ */ React.createElement(
|
|
1912
|
+
Btn,
|
|
1913
|
+
{
|
|
1914
|
+
icon: muted ? "bellOff" : "bell",
|
|
1915
|
+
title: muted ? T && T.unmuteSound || "Turn sound back on for this chat" : T && T.muteSound || "Mute sound for this chat",
|
|
1916
|
+
onClick: () => onToggleMute(peer.id)
|
|
1917
|
+
}
|
|
1918
|
+
), onCall && /* @__PURE__ */ React.createElement(Btn, { icon: "phone", title: T && T.audioCall || "Audio call", onClick: () => onCall(peer.userId, false) }), onCall && /* @__PURE__ */ React.createElement(Btn, { icon: "video", title: T && T.videoCall || "Video call", onClick: () => onCall(peer.userId, true) }), onSendRtcFile && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
|
|
1889
1919
|
"input",
|
|
1890
1920
|
{
|
|
1891
1921
|
ref: rtcFileRef,
|
|
@@ -1896,7 +1926,16 @@ ${peer.address}`
|
|
|
1896
1926
|
e.target.value = "";
|
|
1897
1927
|
}
|
|
1898
1928
|
}
|
|
1899
|
-
), /* @__PURE__ */ React.createElement(Btn, { icon: "file", title: T && T.sendWebrtcFile || "Send file via WebRTC", onClick: () => rtcFileRef.current && rtcFileRef.current.click() })), /* @__PURE__ */ React.createElement("div", { style: { position: "relative" } }, /* @__PURE__ */ React.createElement(Btn, { icon: "more", onClick: () => setMenu((v) => !v) }), menu && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { onClick: () => setMenu(false), style: { position: "fixed", inset: 0, zIndex: 40 } }), /* @__PURE__ */ React.createElement("div", { style: { position: "absolute", right: 0, top: 36, zIndex: 50, width: 180, background: "var(--panel-2)", border: "1px solid var(--line)", borderRadius: 9, padding: 6, boxShadow: "0 14px 40px rgba(0,0,0,0.4)" } }, /* @__PURE__ */ React.createElement(MenuItem, { icon: "
|
|
1929
|
+
), !phone && /* @__PURE__ */ React.createElement(Btn, { icon: "file", title: T && T.sendWebrtcFile || "Send file via WebRTC", onClick: () => rtcFileRef.current && rtcFileRef.current.click() })), /* @__PURE__ */ React.createElement("div", { style: { position: "relative" } }, /* @__PURE__ */ React.createElement(Btn, { icon: "more", onClick: () => setMenu((v) => !v) }), menu && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { onClick: () => setMenu(false), style: { position: "fixed", inset: 0, zIndex: 40 } }), /* @__PURE__ */ React.createElement("div", { style: { position: "absolute", right: 0, top: 36, zIndex: 50, width: 180, background: "var(--panel-2)", border: "1px solid var(--line)", borderRadius: 9, padding: 6, boxShadow: "0 14px 40px rgba(0,0,0,0.4)" } }, phone && onToggleMute && /* @__PURE__ */ React.createElement(MenuItem, { icon: muted ? "bell" : "bellOff", label: muted ? T && T.unmuteSound || "Turn sound back on" : T && T.muteSound || "Mute sound", onClick: () => {
|
|
1930
|
+
setMenu(false);
|
|
1931
|
+
onToggleMute(peer.id);
|
|
1932
|
+
} }), phone && onSendRtcFile && /* @__PURE__ */ React.createElement(MenuItem, { icon: "file", label: T && T.sendWebrtcFile || "Send file via WebRTC", onClick: () => {
|
|
1933
|
+
setMenu(false);
|
|
1934
|
+
rtcFileRef.current && rtcFileRef.current.click();
|
|
1935
|
+
} }), phone && /* @__PURE__ */ React.createElement(MenuItem, { icon: "network", label: T && T.openNet || "Network view", onClick: () => {
|
|
1936
|
+
setMenu(false);
|
|
1937
|
+
onOpenNet(peer);
|
|
1938
|
+
} }), /* @__PURE__ */ React.createElement(MenuItem, { icon: "hash", label: T.alias, onClick: () => {
|
|
1900
1939
|
setMenu(false);
|
|
1901
1940
|
onAlias(peer);
|
|
1902
1941
|
} }), /* @__PURE__ */ React.createElement(MenuItem, { icon: "check", label: T.selectDelete || "Select & delete", onClick: () => {
|
|
@@ -1917,7 +1956,7 @@ ${peer.address}`
|
|
|
1917
1956
|
fontSize: 12.5,
|
|
1918
1957
|
color: "var(--text)"
|
|
1919
1958
|
} }, /* @__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.")),
|
|
1920
|
-
/* @__PURE__ */ React.createElement("div", { ref: scrollRef, onScroll: updateFollowScroll, style: { flex: 1, overflow: "auto", padding: "18px 22px" } }, /* @__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(
|
|
1959
|
+
/* @__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(
|
|
1921
1960
|
Msg,
|
|
1922
1961
|
{
|
|
1923
1962
|
key: m.id || i,
|
|
@@ -1946,7 +1985,7 @@ ${peer.address}`
|
|
|
1946
1985
|
setSelMode(false);
|
|
1947
1986
|
setSel(/* @__PURE__ */ new Set());
|
|
1948
1987
|
} }, T.cancel || "Cancel"), /* @__PURE__ */ React.createElement(Btn, { size: "sm", tone: "danger", icon: "trash", onClick: () => doDelete([...sel]) }, T.delete || "Delete")),
|
|
1949
|
-
/* @__PURE__ */ React.createElement("div", { style: { flexShrink: 0, borderTop: "1px solid var(--line)", padding: "12px 16px", background: "var(--panel)" } }, flash && /* @__PURE__ */ React.createElement("div", { style: {
|
|
1988
|
+
/* @__PURE__ */ React.createElement("div", { style: { flexShrink: 0, borderTop: "1px solid var(--line)", padding: phone ? "8px 8px calc(8px + env(safe-area-inset-bottom, 0px))" : "12px 16px", background: "var(--panel)" } }, flash && /* @__PURE__ */ React.createElement("div", { style: {
|
|
1950
1989
|
maxWidth: 1200,
|
|
1951
1990
|
margin: "0 auto 8px",
|
|
1952
1991
|
display: "flex",
|
|
@@ -1963,7 +2002,7 @@ ${peer.address}`
|
|
|
1963
2002
|
stroke: 2.2,
|
|
1964
2003
|
color: flash.kind === "err" ? "var(--danger)" : "var(--accent)"
|
|
1965
2004
|
}
|
|
1966
|
-
), flash.msg), sending && /* @__PURE__ */ React.createElement("div", { style: { maxWidth: 1200, margin: "0 auto 8px", display: "flex", alignItems: "center", gap: 8, fontFamily: "var(--mono)", fontSize: 11.5, color: "var(--faint)" } }, /* @__PURE__ */ React.createElement(Icon, { name: "paperclip", size: 13, stroke: 2, color: "var(--accent)" }), (lang === "zh" ? "\u53D1\u9001\u4E2D: " : "Sending: ") + (sending.via === "webrtc" ? "WebRTC \xB7 " : "") + sending.name + (sending.size ? " \xB7 " + dkFileSize(sending.sent || 0) + " / " + dkFileSize(sending.size) : "") + (sending.pct != null ? " \xB7 " + Math.min(100, Math.max(0, sending.pct)).toFixed(1) + "%" : "") + (sending.kbps != null ? " \xB7 " + dkSpeed(sending.kbps) : "")), /* @__PURE__ */ React.createElement("div", { style: { maxWidth: 1200, margin: "0 auto", display: "flex", alignItems: "center", gap: 10 } }, /* @__PURE__ */ React.createElement(
|
|
2005
|
+
), flash.msg), sending && /* @__PURE__ */ React.createElement("div", { style: { maxWidth: 1200, margin: "0 auto 8px", display: "flex", alignItems: "center", gap: 8, fontFamily: "var(--mono)", fontSize: 11.5, color: "var(--faint)" } }, /* @__PURE__ */ React.createElement(Icon, { name: "paperclip", size: 13, stroke: 2, color: "var(--accent)" }), (lang === "zh" ? "\u53D1\u9001\u4E2D: " : "Sending: ") + (sending.via === "webrtc" ? "WebRTC \xB7 " : "") + sending.name + (sending.size ? " \xB7 " + dkFileSize(sending.sent || 0) + " / " + dkFileSize(sending.size) : "") + (sending.pct != null ? " \xB7 " + Math.min(100, Math.max(0, sending.pct)).toFixed(1) + "%" : "") + (sending.kbps != null ? " \xB7 " + dkSpeed(sending.kbps) : "")), /* @__PURE__ */ React.createElement("div", { style: { maxWidth: 1200, margin: "0 auto", display: "flex", alignItems: "center", gap: phone ? 6 : 10 } }, /* @__PURE__ */ React.createElement(
|
|
1967
2006
|
"input",
|
|
1968
2007
|
{
|
|
1969
2008
|
ref: fileRef,
|
|
@@ -1985,7 +2024,7 @@ ${peer.address}`
|
|
|
1985
2024
|
sendDraft();
|
|
1986
2025
|
}
|
|
1987
2026
|
},
|
|
1988
|
-
placeholder: `${T.message} ${peer.alias || shortKey(peer.userId, 6, 4)}\u2026`,
|
|
2027
|
+
placeholder: phone ? `${T.message}\u2026` : `${T.message} ${peer.alias || shortKey(peer.userId, 6, 4)}\u2026`,
|
|
1989
2028
|
style: { flex: 1, height: 38, borderRadius: 9, border: "1px solid var(--line)", background: "var(--panel-2)", color: "var(--text)", fontFamily: "var(--ui)", fontSize: 13.5, padding: "0 14px", outline: "none", minWidth: 0 }
|
|
1990
2029
|
}
|
|
1991
2030
|
), /* @__PURE__ */ React.createElement(Btn, { tone: "solid", icon: "arrowUp", title: T.send, onClick: sendDraft }))),
|
|
@@ -2077,9 +2116,12 @@ function MenuItem({ icon, label, onClick, danger }) {
|
|
|
2077
2116
|
function ChatEmpty({ T }) {
|
|
2078
2117
|
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));
|
|
2079
2118
|
}
|
|
2080
|
-
function ChatTab({ T, lang, peers, requests, activeId, thread, onSelect, onAct, onAdd, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread, prefillAddr, onPrefillConsumed }) {
|
|
2119
|
+
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 }) {
|
|
2081
2120
|
const peer = peers.find((p) => p.id === activeId);
|
|
2082
|
-
|
|
2121
|
+
if (phone) {
|
|
2122
|
+
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 });
|
|
2123
|
+
}
|
|
2124
|
+
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 }));
|
|
2083
2125
|
}
|
|
2084
2126
|
Object.assign(window, { ChatTab });
|
|
2085
2127
|
|
|
@@ -2117,14 +2159,14 @@ function CopyBtn({ value, copiedText = "Copied", copyFailedText = "Copy failed",
|
|
|
2117
2159
|
));
|
|
2118
2160
|
}
|
|
2119
2161
|
function FieldRow({ T, label, value, mono = true, copy, qr, onQr, last }) {
|
|
2120
|
-
return /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 14, padding: "13px 16px", borderBottom: last ? "none" : "1px solid var(--line)" } }, /* @__PURE__ */ React.createElement("span", { style: { width: 130, flexShrink: 0, fontFamily: "var(--mono)", fontSize: 11.5, fontWeight: 600, color: "var(--faint)", textTransform: "uppercase", letterSpacing: 0.5 } }, label), /* @__PURE__ */ React.createElement("span", { style: { flex: 1, minWidth: 0, fontFamily: mono ? "var(--mono)" : "var(--ui)", fontSize: 13.5, color: "var(--text)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, value), copy && /* @__PURE__ */ React.createElement(CopyBtn, { value, copiedText: T.copied, copyFailedText: T.copyFailed, copyTitle: T.copy }), qr && /* @__PURE__ */ React.createElement(Btn, { icon: "qr", size: "sm", onClick: () => onQr && onQr(value, label) }));
|
|
2162
|
+
return /* @__PURE__ */ React.createElement("div", { className: "dk-row", style: { display: "flex", alignItems: "center", gap: 14, padding: "13px 16px", borderBottom: last ? "none" : "1px solid var(--line)" } }, /* @__PURE__ */ React.createElement("span", { className: "dk-lbl", style: { width: 130, flexShrink: 0, fontFamily: "var(--mono)", fontSize: 11.5, fontWeight: 600, color: "var(--faint)", textTransform: "uppercase", letterSpacing: 0.5 } }, label), /* @__PURE__ */ React.createElement("span", { style: { flex: 1, minWidth: 0, fontFamily: mono ? "var(--mono)" : "var(--ui)", fontSize: 13.5, color: "var(--text)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, value), copy && /* @__PURE__ */ React.createElement(CopyBtn, { value, copiedText: T.copied, copyFailedText: T.copyFailed, copyTitle: T.copy }), qr && /* @__PURE__ */ React.createElement(Btn, { icon: "qr", size: "sm", onClick: () => onQr && onQr(value, label) }));
|
|
2121
2163
|
}
|
|
2122
2164
|
function DkVersionRow({ T, me }) {
|
|
2123
2165
|
const unsure = me.verFromBackend === false;
|
|
2124
2166
|
const pill = (name, ver, mark) => /* @__PURE__ */ React.createElement("span", { key: name, style: { display: "inline-flex", alignItems: "baseline", gap: 5 } }, /* @__PURE__ */ React.createElement("span", { style: { color: "var(--faint)" } }, name), /* @__PURE__ */ React.createElement("span", { title: mark && unsure ? T.verInstalledHint : void 0 }, ver || "\u2014", mark && unsure && ver ? /* @__PURE__ */ React.createElement("span", { style: { color: "var(--faint)" } }, " ?") : null));
|
|
2125
2167
|
const line = (label, items) => /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "baseline", gap: 10, flexWrap: "wrap" } }, /* @__PURE__ */ React.createElement("span", { style: { width: 52, flexShrink: 0, fontSize: 11, color: "var(--faint)" } }, label), /* @__PURE__ */ React.createElement("span", { style: { display: "flex", gap: 12, flexWrap: "wrap" } }, items));
|
|
2126
2168
|
const embedded = me.backend === "embedded";
|
|
2127
|
-
return /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "flex-start", gap: 14, padding: "13px 16px", borderBottom: "1px solid var(--line)" } }, /* @__PURE__ */ React.createElement("span", { style: { width: 130, flexShrink: 0, fontFamily: "var(--mono)", fontSize: 11.5, fontWeight: 600, color: "var(--faint)", textTransform: "uppercase", letterSpacing: 0.5 } }, T.version), /* @__PURE__ */ React.createElement("div", { style: { flex: 1, minWidth: 0, display: "flex", flexDirection: "column", gap: 6, fontFamily: "var(--mono)", fontSize: 13, color: "var(--text)" } }, line(T.verApp || "app", [
|
|
2169
|
+
return /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "flex-start", gap: 14, padding: "13px 16px", borderBottom: "1px solid var(--line)" } }, /* @__PURE__ */ React.createElement("span", { className: "dk-lbl", style: { width: 130, flexShrink: 0, fontFamily: "var(--mono)", fontSize: 11.5, fontWeight: 600, color: "var(--faint)", textTransform: "uppercase", letterSpacing: 0.5 } }, T.version), /* @__PURE__ */ React.createElement("div", { style: { flex: 1, minWidth: 0, display: "flex", flexDirection: "column", gap: 6, fontFamily: "var(--mono)", fontSize: 13, color: "var(--text)" } }, line(T.verApp || "app", [
|
|
2128
2170
|
pill("beagle", me.beagleVer),
|
|
2129
2171
|
pill("peer-webrtc", me.webrtcVer),
|
|
2130
2172
|
...embedded ? [pill("peer", me.peerVer)] : []
|
|
@@ -2428,6 +2470,47 @@ function DkPublicProfile({ T, userid, fallbackName, onClose, isFriend, isMe, onM
|
|
|
2428
2470
|
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")
|
|
2429
2471
|
))))));
|
|
2430
2472
|
}
|
|
2473
|
+
function dkAvatarDataUrl(file) {
|
|
2474
|
+
return new Promise((resolve, reject) => {
|
|
2475
|
+
const img = new Image();
|
|
2476
|
+
const url = URL.createObjectURL(file);
|
|
2477
|
+
img.onload = () => {
|
|
2478
|
+
URL.revokeObjectURL(url);
|
|
2479
|
+
try {
|
|
2480
|
+
const S = 256;
|
|
2481
|
+
const c = document.createElement("canvas");
|
|
2482
|
+
c.width = S;
|
|
2483
|
+
c.height = S;
|
|
2484
|
+
const ctx = c.getContext("2d");
|
|
2485
|
+
const side = Math.min(img.width, img.height);
|
|
2486
|
+
ctx.drawImage(img, (img.width - side) / 2, (img.height - side) / 2, side, side, 0, 0, S, S);
|
|
2487
|
+
const enc = (q) => {
|
|
2488
|
+
let d = c.toDataURL("image/webp", q);
|
|
2489
|
+
if (!d.startsWith("data:image/webp"))
|
|
2490
|
+
d = c.toDataURL("image/jpeg", q);
|
|
2491
|
+
return d;
|
|
2492
|
+
};
|
|
2493
|
+
let dataUrl = null;
|
|
2494
|
+
for (const q of [0.9, 0.75, 0.55, 0.4]) {
|
|
2495
|
+
dataUrl = enc(q);
|
|
2496
|
+
if (dataUrl.length < 13e4)
|
|
2497
|
+
break;
|
|
2498
|
+
}
|
|
2499
|
+
if (!dataUrl || dataUrl.length >= 13e4)
|
|
2500
|
+
reject(new Error("too-big"));
|
|
2501
|
+
else
|
|
2502
|
+
resolve(dataUrl);
|
|
2503
|
+
} catch (err) {
|
|
2504
|
+
reject(err);
|
|
2505
|
+
}
|
|
2506
|
+
};
|
|
2507
|
+
img.onerror = () => {
|
|
2508
|
+
URL.revokeObjectURL(url);
|
|
2509
|
+
reject(new Error("bad-image"));
|
|
2510
|
+
};
|
|
2511
|
+
img.src = url;
|
|
2512
|
+
});
|
|
2513
|
+
}
|
|
2431
2514
|
function DkEnsCard({ T, me, onRecord }) {
|
|
2432
2515
|
const [st, setSt] = React.useState({ loading: true });
|
|
2433
2516
|
const [label, setLabel] = React.useState("");
|
|
@@ -2510,7 +2593,7 @@ function DkEnsCard({ T, me, onRecord }) {
|
|
|
2510
2593
|
var _a;
|
|
2511
2594
|
const saved = (st.record && st.record.texts || {})["com." + k] || "";
|
|
2512
2595
|
const cur = (_a = draft[k]) != null ? _a : "";
|
|
2513
|
-
return /* @__PURE__ */ React.createElement("div", { key: k, style: { ...row, borderBottom: last ? "none" : row.borderBottom } }, /* @__PURE__ */ React.createElement("span", { style: rowLbl }, k), /* @__PURE__ */ React.createElement(
|
|
2596
|
+
return /* @__PURE__ */ React.createElement("div", { key: k, className: "dk-row", style: { ...row, borderBottom: last ? "none" : row.borderBottom } }, /* @__PURE__ */ React.createElement("span", { className: "dk-lbl", style: rowLbl }, k), /* @__PURE__ */ React.createElement(
|
|
2514
2597
|
"input",
|
|
2515
2598
|
{
|
|
2516
2599
|
value: cur,
|
|
@@ -2553,51 +2636,14 @@ function DkEnsCard({ T, me, onRecord }) {
|
|
|
2553
2636
|
return;
|
|
2554
2637
|
setBusy(true);
|
|
2555
2638
|
setMsg(null);
|
|
2556
|
-
|
|
2557
|
-
const url = URL.createObjectURL(f);
|
|
2558
|
-
const fail = (text) => {
|
|
2639
|
+
dkAvatarDataUrl(f).then((dataUrl) => post("/api/ens-avatar-upload", { dataUrl })).catch((err) => {
|
|
2559
2640
|
setBusy(false);
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
URL.revokeObjectURL(url);
|
|
2564
|
-
try {
|
|
2565
|
-
const S = 256;
|
|
2566
|
-
const c = document.createElement("canvas");
|
|
2567
|
-
c.width = S;
|
|
2568
|
-
c.height = S;
|
|
2569
|
-
const ctx = c.getContext("2d");
|
|
2570
|
-
const side = Math.min(img.width, img.height);
|
|
2571
|
-
ctx.drawImage(img, (img.width - side) / 2, (img.height - side) / 2, side, side, 0, 0, S, S);
|
|
2572
|
-
const enc = (q) => {
|
|
2573
|
-
let d = c.toDataURL("image/webp", q);
|
|
2574
|
-
if (!d.startsWith("data:image/webp"))
|
|
2575
|
-
d = c.toDataURL("image/jpeg", q);
|
|
2576
|
-
return d;
|
|
2577
|
-
};
|
|
2578
|
-
let dataUrl = null;
|
|
2579
|
-
for (const q of [0.9, 0.75, 0.55, 0.4]) {
|
|
2580
|
-
dataUrl = enc(q);
|
|
2581
|
-
if (dataUrl.length < 13e4)
|
|
2582
|
-
break;
|
|
2583
|
-
}
|
|
2584
|
-
if (!dataUrl || dataUrl.length >= 13e4) {
|
|
2585
|
-
fail(T.ensTooBig || "image too large even after scaling");
|
|
2586
|
-
return;
|
|
2587
|
-
}
|
|
2588
|
-
post("/api/ens-avatar-upload", { dataUrl });
|
|
2589
|
-
} catch (err) {
|
|
2590
|
-
fail(String(err && err.message || err));
|
|
2591
|
-
}
|
|
2592
|
-
};
|
|
2593
|
-
img.onerror = () => {
|
|
2594
|
-
URL.revokeObjectURL(url);
|
|
2595
|
-
fail(T.ensBadImage || "cannot read that image");
|
|
2596
|
-
};
|
|
2597
|
-
img.src = url;
|
|
2641
|
+
const m = err && err.message;
|
|
2642
|
+
setMsg({ tone: "err", text: m === "too-big" ? T.ensTooBig || "image too large even after scaling" : m === "bad-image" ? T.ensBadImage || "cannot read that image" : String(m || err) });
|
|
2643
|
+
});
|
|
2598
2644
|
};
|
|
2599
|
-
const walletRow = (lbl, chain, bound, onBind, last) => /* @__PURE__ */ React.createElement("div", { style: { ...row, borderBottom: last ? "none" : row.borderBottom } }, /* @__PURE__ */ React.createElement("span", { style: { width: 130, flexShrink: 0, fontFamily: "var(--mono)", fontSize: 11.5, fontWeight: 600, color: "var(--faint)", textTransform: "uppercase", letterSpacing: 0.5 } }, lbl), bound ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", { style: { flex: 1, minWidth: 0, fontFamily: "var(--mono)", fontSize: 12.5, color: "var(--text)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, bound), /* @__PURE__ */ React.createElement(CopyBtn, { value: bound, copiedText: T.copied, copyFailedText: T.copyFailed, copyTitle: T.copy }), /* @__PURE__ */ React.createElement(Btn, { size: "sm", tone: "danger", disabled: busy || !st.mineOwned, onClick: () => bind(chain, "") }, T.ensUnbind || "unbind")) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", { style: { flex: 1, fontFamily: "var(--ui)", fontSize: 12, color: "var(--faint)" } }, T.ensNotBound || "not bound"), /* @__PURE__ */ React.createElement(Btn, { size: "sm", disabled: busy || !st.mineOwned, onClick: onBind }, T.ensBind || "bind")));
|
|
2600
|
-
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Card, { label: T.ensCard || "Name \xB7 beagles.eth" }, st.loading ? /* @__PURE__ */ React.createElement("div", { style: { padding: "14px 16px", fontFamily: "var(--ui)", fontSize: 12.5, color: "var(--faint)" } }, T.dirLoading || "loading\u2026") : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { style: row }, /* @__PURE__ */ React.createElement("span", { style: { width: 130, flexShrink: 0, fontFamily: "var(--mono)", fontSize: 11.5, fontWeight: 600, color: "var(--faint)", textTransform: "uppercase", letterSpacing: 0.5 } }, T.ensName || "name"), /* @__PURE__ */ React.createElement(
|
|
2645
|
+
const walletRow = (lbl, chain, bound, onBind, last) => /* @__PURE__ */ React.createElement("div", { className: "dk-row", style: { ...row, borderBottom: last ? "none" : row.borderBottom } }, /* @__PURE__ */ React.createElement("span", { className: "dk-lbl", style: { width: 130, flexShrink: 0, fontFamily: "var(--mono)", fontSize: 11.5, fontWeight: 600, color: "var(--faint)", textTransform: "uppercase", letterSpacing: 0.5 } }, lbl), bound ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", { style: { flex: 1, minWidth: 0, fontFamily: "var(--mono)", fontSize: 12.5, color: "var(--text)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, bound), /* @__PURE__ */ React.createElement(CopyBtn, { value: bound, copiedText: T.copied, copyFailedText: T.copyFailed, copyTitle: T.copy }), /* @__PURE__ */ React.createElement(Btn, { size: "sm", tone: "danger", disabled: busy || !st.mineOwned, onClick: () => bind(chain, "") }, T.ensUnbind || "unbind")) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", { style: { flex: 1, fontFamily: "var(--ui)", fontSize: 12, color: "var(--faint)" } }, T.ensNotBound || "not bound"), /* @__PURE__ */ React.createElement(Btn, { size: "sm", disabled: busy || !st.mineOwned, onClick: onBind }, T.ensBind || "bind")));
|
|
2646
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Card, { label: T.ensCard || "Name \xB7 beagles.eth" }, st.loading ? /* @__PURE__ */ React.createElement("div", { style: { padding: "14px 16px", fontFamily: "var(--ui)", fontSize: 12.5, color: "var(--faint)" } }, T.dirLoading || "loading\u2026") : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "dk-row dk-wrap", style: row }, /* @__PURE__ */ React.createElement("span", { className: "dk-lbl", style: { width: 130, flexShrink: 0, fontFamily: "var(--mono)", fontSize: 11.5, fontWeight: 600, color: "var(--faint)", textTransform: "uppercase", letterSpacing: 0.5 } }, T.ensName || "name"), /* @__PURE__ */ React.createElement(
|
|
2601
2647
|
"input",
|
|
2602
2648
|
{
|
|
2603
2649
|
value: label,
|
|
@@ -2608,9 +2654,9 @@ function DkEnsCard({ T, me, onRecord }) {
|
|
|
2608
2654
|
if (e.key === "Enter")
|
|
2609
2655
|
register();
|
|
2610
2656
|
},
|
|
2611
|
-
style: field
|
|
2657
|
+
style: { ...field, minWidth: 110 }
|
|
2612
2658
|
}
|
|
2613
|
-
), /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 12.5, color: "var(--dim)", flexShrink: 0 } }, ".beagles.eth"), /* @__PURE__ */ React.createElement(Btn, { tone: "accent", size: "sm", disabled: busy || !label.trim(), onClick: register }, busy ? "\u2026" : st.registered && st.mineOwned ? T.ensUpdate || "update" : T.ensRegister || "register")), st.registered && !st.mineOwned && /* @__PURE__ */ React.createElement("div", { style: { padding: "10px 16px", borderBottom: "1px solid var(--line)", fontFamily: "var(--ui)", fontSize: 11.5, color: "var(--faint)" } }, (T.ensWalletOwned || "registered via your mobile wallet as") + " ", /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)" } }, rec && rec.name)), st.registered && /* @__PURE__ */ React.createElement("div", { style: row }, /* @__PURE__ */ React.createElement("span", { style: { width: 130, flexShrink: 0, fontFamily: "var(--mono)", fontSize: 11.5, fontWeight: 600, color: "var(--faint)", textTransform: "uppercase", letterSpacing: 0.5 } }, T.ensAvatar || "avatar"), upAvatar ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("img", { src: upAvatar, alt: "", width: 34, height: 34, style: { width: 34, height: 34, borderRadius: 9, objectFit: "cover", flexShrink: 0, background: "var(--panel-2)" } }), /* @__PURE__ */ React.createElement("span", { style: { flex: 1, minWidth: 0, fontFamily: "var(--ui)", fontSize: 12.5, color: "var(--text)" } }, T.ensCustom || "custom image"), /* @__PURE__ */ React.createElement(Btn, { size: "sm", tone: "danger", disabled: busy || !st.mineOwned, onClick: () => post("/api/ens-avatar", { nftid: null }) }, T.ensClear || "clear")) : punkId != null ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(DkPunkAvatar, { id: punkId, size: 34, radius: 9, fallbackSeed: me.userId }), /* @__PURE__ */ React.createElement("span", { style: { flex: 1, minWidth: 0, fontFamily: "var(--mono)", fontSize: 12.5, color: "var(--text)" } }, `CryptoPunk #${punkId}`), /* @__PURE__ */ React.createElement(Btn, { size: "sm", tone: "danger", disabled: busy || !st.mineOwned, onClick: () => post("/api/ens-avatar", { nftid: null }) }, T.ensClear || "clear")) : /* @__PURE__ */ React.createElement("span", { style: { flex: 1, fontFamily: "var(--ui)", fontSize: 12, color: "var(--faint)" } }, T.ensNotSet || "not set"), /* @__PURE__ */ React.createElement(Btn, { size: "sm", disabled: busy || !st.mineOwned, onClick: () => fileRef.current && fileRef.current.click() }, T.ensUpload || "upload"), /* @__PURE__ */ React.createElement(Btn, { size: "sm", disabled: busy || !st.mineOwned, onClick: () => setPicking(true) }, T.ensChoose || "choose"), /* @__PURE__ */ React.createElement("input", { ref: fileRef, type: "file", accept: "image/*", onChange: onUploadFile, style: { display: "none" } })), walletRow(T.ensEth || "ethereum", "eth", boundEth, bindEth, false), walletRow(T.ensSol || "solana", "sol", boundSol, bindSol, !msg), msg && /* @__PURE__ */ React.createElement("div", { style: { padding: "10px 16px", fontFamily: "var(--ui)", fontSize: 12, color: msg.tone === "ok" ? "var(--online)" : "var(--danger)" } }, msg.text)), picking && /* @__PURE__ */ React.createElement(DkPunkPicker, { T, onPick: pickPunk, onClose: () => setPicking(false) })), !st.loading && st.registered && /* @__PURE__ */ React.createElement(Card, { label: T.linksCard || "Links" }, SOCIALS.map((s, i) => socialRow(s, i === SOCIALS.length - 1))));
|
|
2659
|
+
), /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 12.5, color: "var(--dim)", flexShrink: 0 } }, ".beagles.eth"), /* @__PURE__ */ React.createElement(Btn, { tone: "accent", size: "sm", disabled: busy || !label.trim(), onClick: register }, busy ? "\u2026" : st.registered && st.mineOwned ? T.ensUpdate || "update" : T.ensRegister || "register")), st.registered && !st.mineOwned && /* @__PURE__ */ React.createElement("div", { style: { padding: "10px 16px", borderBottom: "1px solid var(--line)", fontFamily: "var(--ui)", fontSize: 11.5, color: "var(--faint)" } }, (T.ensWalletOwned || "registered via your mobile wallet as") + " ", /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)" } }, rec && rec.name)), st.registered && /* @__PURE__ */ React.createElement("div", { className: "dk-row dk-wrap", style: row }, /* @__PURE__ */ React.createElement("span", { className: "dk-lbl", style: { width: 130, flexShrink: 0, fontFamily: "var(--mono)", fontSize: 11.5, fontWeight: 600, color: "var(--faint)", textTransform: "uppercase", letterSpacing: 0.5 } }, T.ensAvatar || "avatar"), upAvatar ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("img", { src: upAvatar, alt: "", width: 34, height: 34, style: { width: 34, height: 34, borderRadius: 9, objectFit: "cover", flexShrink: 0, background: "var(--panel-2)" } }), /* @__PURE__ */ React.createElement("span", { style: { flex: 1, minWidth: 0, fontFamily: "var(--ui)", fontSize: 12.5, color: "var(--text)" } }, T.ensCustom || "custom image"), /* @__PURE__ */ React.createElement(Btn, { size: "sm", tone: "danger", disabled: busy || !st.mineOwned, onClick: () => post("/api/ens-avatar", { nftid: null }) }, T.ensClear || "clear")) : punkId != null ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(DkPunkAvatar, { id: punkId, size: 34, radius: 9, fallbackSeed: me.userId }), /* @__PURE__ */ React.createElement("span", { style: { flex: 1, minWidth: 0, fontFamily: "var(--mono)", fontSize: 12.5, color: "var(--text)" } }, `CryptoPunk #${punkId}`), /* @__PURE__ */ React.createElement(Btn, { size: "sm", tone: "danger", disabled: busy || !st.mineOwned, onClick: () => post("/api/ens-avatar", { nftid: null }) }, T.ensClear || "clear")) : /* @__PURE__ */ React.createElement("span", { style: { flex: 1, fontFamily: "var(--ui)", fontSize: 12, color: "var(--faint)" } }, T.ensNotSet || "not set"), /* @__PURE__ */ React.createElement(Btn, { size: "sm", disabled: busy || !st.mineOwned, onClick: () => fileRef.current && fileRef.current.click() }, T.ensUpload || "upload"), /* @__PURE__ */ React.createElement(Btn, { size: "sm", disabled: busy || !st.mineOwned, onClick: () => setPicking(true) }, T.ensChoose || "choose"), /* @__PURE__ */ React.createElement("input", { ref: fileRef, type: "file", accept: "image/*", onChange: onUploadFile, style: { display: "none" } })), walletRow(T.ensEth || "ethereum", "eth", boundEth, bindEth, false), walletRow(T.ensSol || "solana", "sol", boundSol, bindSol, !msg), msg && /* @__PURE__ */ React.createElement("div", { style: { padding: "10px 16px", fontFamily: "var(--ui)", fontSize: 12, color: msg.tone === "ok" ? "var(--online)" : "var(--danger)" } }, msg.text)), picking && /* @__PURE__ */ React.createElement(DkPunkPicker, { T, onPick: pickPunk, onClose: () => setPicking(false) })), !st.loading && st.registered && /* @__PURE__ */ React.createElement(Card, { label: T.linksCard || "Links" }, SOCIALS.map((s, i) => socialRow(s, i === SOCIALS.length - 1))));
|
|
2614
2660
|
}
|
|
2615
2661
|
function DkUpdateCheck({ T }) {
|
|
2616
2662
|
const [st, setSt] = React.useState("idle");
|
|
@@ -2626,7 +2672,7 @@ function DkUpdateCheck({ T }) {
|
|
|
2626
2672
|
setSt("latest");
|
|
2627
2673
|
}).catch(() => setSt("idle"));
|
|
2628
2674
|
};
|
|
2629
|
-
return /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 14, padding: "13px 16px" } }, /* @__PURE__ */ React.createElement("span", { style: { width: 130, flexShrink: 0, fontFamily: "var(--mono)", fontSize: 11.5, fontWeight: 600, color: "var(--faint)", textTransform: "uppercase", letterSpacing: 0.5 } }, T.updCheckLabel || "updates"), /* @__PURE__ */ React.createElement("span", { style: { flex: 1, fontFamily: "var(--ui)", fontSize: 12, color: st === "latest" ? "var(--online)" : "var(--faint)" } }, st === "latest" ? T.updLatest || "up to date \u2713" : ""), /* @__PURE__ */ React.createElement(Btn, { size: "sm", disabled: st === "busy", onClick: check }, st === "busy" ? "\u2026" : T.updCheck || "check for updates"), info && /* @__PURE__ */ React.createElement(DkUpdateModal, { T, info, onClose: () => setInfo(null) }));
|
|
2675
|
+
return /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 14, padding: "13px 16px" } }, /* @__PURE__ */ React.createElement("span", { className: "dk-lbl", style: { width: 130, flexShrink: 0, fontFamily: "var(--mono)", fontSize: 11.5, fontWeight: 600, color: "var(--faint)", textTransform: "uppercase", letterSpacing: 0.5 } }, T.updCheckLabel || "updates"), /* @__PURE__ */ React.createElement("span", { style: { flex: 1, fontFamily: "var(--ui)", fontSize: 12, color: st === "latest" ? "var(--online)" : "var(--faint)" } }, st === "latest" ? T.updLatest || "up to date \u2713" : ""), /* @__PURE__ */ React.createElement(Btn, { size: "sm", disabled: st === "busy", onClick: check }, st === "busy" ? "\u2026" : T.updCheck || "check for updates"), info && /* @__PURE__ */ React.createElement(DkUpdateModal, { T, info, onClose: () => setInfo(null) }));
|
|
2630
2676
|
}
|
|
2631
2677
|
function DkKeyBackup({ t }) {
|
|
2632
2678
|
const zh = t && t.lang === "zh";
|
|
@@ -2739,7 +2785,7 @@ function DkDisplaySettings({ t, setTweak }) {
|
|
|
2739
2785
|
},
|
|
2740
2786
|
label
|
|
2741
2787
|
)));
|
|
2742
|
-
return /* @__PURE__ */ React.createElement(Card, { label: zh ? "\u663E\u793A" : "Display" }, /* @__PURE__ */ React.createElement("div", { style: { ...row, borderBottom: "1px solid var(--line)" } }, /* @__PURE__ */ React.createElement("span", { style: name }, zh ? "\u8BED\u8A00" : "Language"), seg("lang", t.lang, (v) => setTweak("lang", v), [["en", "EN"], ["zh", "\u4E2D\u6587"]])), /* @__PURE__ */ React.createElement("div", { style: row }, /* @__PURE__ */ React.createElement("span", { style: name }, zh ? "\u4E3B\u9898" : "Theme"), seg("theme", t.theme, (v) => setTweak("theme", v), [["dark", zh ? "\u6DF1\u8272" : "Dark"], ["light", zh ? "\u6D45\u8272" : "Light"]])));
|
|
2788
|
+
return /* @__PURE__ */ React.createElement(Card, { label: zh ? "\u663E\u793A" : "Display" }, /* @__PURE__ */ React.createElement("div", { style: { ...row, borderBottom: "1px solid var(--line)" } }, /* @__PURE__ */ React.createElement("span", { style: name }, zh ? "\u8BED\u8A00" : "Language"), seg("lang", t.lang, (v) => setTweak("lang", v), [["en", "EN"], ["zh", "\u4E2D\u6587"]])), /* @__PURE__ */ React.createElement("div", { className: "dk-row dk-wrap", style: row }, /* @__PURE__ */ React.createElement("span", { style: name }, zh ? "\u4E3B\u9898" : "Theme"), seg("theme", t.theme, (v) => setTweak("theme", v), [["dark", zh ? "\u6DF1\u8272" : "Dark"], ["light", zh ? "\u6D45\u8272" : "Light"]])));
|
|
2743
2789
|
}
|
|
2744
2790
|
function DkAlertSettings({ t, setTweak }) {
|
|
2745
2791
|
const zh = t.lang === "zh";
|
|
@@ -2770,8 +2816,67 @@ function DkAlertSettings({ t, setTweak }) {
|
|
|
2770
2816
|
true
|
|
2771
2817
|
));
|
|
2772
2818
|
}
|
|
2773
|
-
function
|
|
2819
|
+
function DkAvatarSheet({ T, me, zh, onSet, onClose }) {
|
|
2820
|
+
const [picking, setPicking] = React.useState(false);
|
|
2821
|
+
const [busy, setBusy] = React.useState(false);
|
|
2822
|
+
const [err, setErr] = React.useState(null);
|
|
2823
|
+
const fileRef = React.useRef(null);
|
|
2824
|
+
const canUpload = Object.prototype.hasOwnProperty.call(me, "avatarDataUrl");
|
|
2825
|
+
const save = (p) => {
|
|
2826
|
+
setBusy(true);
|
|
2827
|
+
setErr(null);
|
|
2828
|
+
return Promise.resolve(onSet(p)).then((r) => {
|
|
2829
|
+
setBusy(false);
|
|
2830
|
+
if (r && r.ok === false)
|
|
2831
|
+
setErr(r.error || "failed");
|
|
2832
|
+
else
|
|
2833
|
+
onClose();
|
|
2834
|
+
}).catch((e) => {
|
|
2835
|
+
setBusy(false);
|
|
2836
|
+
setErr(String(e && e.message || e));
|
|
2837
|
+
});
|
|
2838
|
+
};
|
|
2839
|
+
const pickPunk = (id) => {
|
|
2840
|
+
setPicking(false);
|
|
2841
|
+
save(canUpload ? { punkId: id, avatarDataUrl: null } : { punkId: id });
|
|
2842
|
+
};
|
|
2843
|
+
const onFile = (e) => {
|
|
2844
|
+
const f = e.target.files && e.target.files[0];
|
|
2845
|
+
e.target.value = "";
|
|
2846
|
+
if (!f)
|
|
2847
|
+
return;
|
|
2848
|
+
setBusy(true);
|
|
2849
|
+
setErr(null);
|
|
2850
|
+
dkAvatarDataUrl(f).then((dataUrl) => save({ avatarDataUrl: dataUrl })).catch((x) => {
|
|
2851
|
+
setBusy(false);
|
|
2852
|
+
const m = x && x.message;
|
|
2853
|
+
setErr(m === "too-big" ? T.ensTooBig || "image too large even after scaling" : m === "bad-image" ? T.ensBadImage || "cannot read that image" : String(m || x));
|
|
2854
|
+
});
|
|
2855
|
+
};
|
|
2856
|
+
const has = me.punkId != null || !!(canUpload && me.avatarDataUrl);
|
|
2857
|
+
const item = (icon, label, onClick, danger) => /* @__PURE__ */ React.createElement("button", { onClick, disabled: busy, style: {
|
|
2858
|
+
display: "flex",
|
|
2859
|
+
alignItems: "center",
|
|
2860
|
+
gap: 12,
|
|
2861
|
+
width: "100%",
|
|
2862
|
+
padding: "13px 14px",
|
|
2863
|
+
border: "none",
|
|
2864
|
+
borderRadius: 10,
|
|
2865
|
+
background: "var(--panel-2)",
|
|
2866
|
+
cursor: busy ? "default" : "pointer",
|
|
2867
|
+
opacity: busy ? 0.6 : 1,
|
|
2868
|
+
fontFamily: "var(--ui)",
|
|
2869
|
+
fontSize: 14,
|
|
2870
|
+
color: danger ? "var(--danger)" : "var(--text)",
|
|
2871
|
+
textAlign: "left"
|
|
2872
|
+
} }, /* @__PURE__ */ React.createElement(Icon, { name: icon, size: 18, stroke: 2, color: danger ? "var(--danger)" : "var(--accent)" }), label);
|
|
2873
|
+
if (picking)
|
|
2874
|
+
return /* @__PURE__ */ React.createElement(DkPunkPicker, { T, onPick: pickPunk, onClose: () => setPicking(false) });
|
|
2875
|
+
return /* @__PURE__ */ React.createElement("div", { onClick: onClose, style: { position: "fixed", inset: 0, zIndex: 90, background: "color-mix(in oklab, #000, transparent 38%)", display: "flex", alignItems: "center", justifyContent: "center", padding: 16 } }, /* @__PURE__ */ React.createElement("div", { onClick: (e) => e.stopPropagation(), style: { width: 360, maxWidth: "100%", background: "var(--panel)", border: "1px solid var(--line)", borderRadius: 16, padding: 16, display: "flex", flexDirection: "column", gap: 8 } }, /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 10, marginBottom: 4 } }, /* @__PURE__ */ React.createElement(DkAvatar, { peer: { ...me, id: me.userId, agent: false }, size: 40, radius: 10, dot: false }), /* @__PURE__ */ React.createElement("span", { style: { flex: 1, fontFamily: "var(--mono)", fontSize: 13.5, fontWeight: 700, color: "var(--text)" } }, zh ? "\u66F4\u6362\u5934\u50CF" : "Change avatar"), /* @__PURE__ */ React.createElement(Btn, { icon: "x", size: "sm", onClick: onClose })), item("sparkles", zh ? "\u9009\u4E00\u4E2A CryptoPunk" : "Choose a CryptoPunk", () => setPicking(true)), canUpload && item("image", zh ? "\u4E0A\u4F20\u7167\u7247" : "Upload a photo", () => fileRef.current && fileRef.current.click()), has && item("trash", zh ? "\u79FB\u9664\u5934\u50CF" : "Remove avatar", () => save(canUpload ? { punkId: null, avatarDataUrl: null } : { punkId: null }), true), /* @__PURE__ */ React.createElement("input", { ref: fileRef, type: "file", accept: "image/*", onChange: onFile, style: { display: "none" } }), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 11.5, color: "var(--faint)", lineHeight: 1.5, marginTop: 2 } }, zh ? "\u4E0D\u9700\u8981\u6CE8\u518C beagles.eth \u540D\u5B57\u3002\u597D\u53CB\u4F1A\u5728\u4E0B\u6B21\u6536\u5230\u4F60\u7684\u8D44\u6599\u65F6\u770B\u5230\u65B0\u5934\u50CF\u3002" : "No beagles.eth name needed. Friends see it the next time your profile reaches them."), err && /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 11.5, color: "var(--danger)" } }, err)));
|
|
2876
|
+
}
|
|
2877
|
+
function ProfileTab({ T, me, onEdit, t, setTweak, onSetAvatar }) {
|
|
2774
2878
|
var _a;
|
|
2879
|
+
const [avatarSheet, setAvatarSheet] = React.useState(false);
|
|
2775
2880
|
const [qr, setQr] = React.useState(null);
|
|
2776
2881
|
const [editing, setEditing] = React.useState(false);
|
|
2777
2882
|
const [ensRec, setEnsRec] = React.useState(null);
|
|
@@ -2779,7 +2884,17 @@ function ProfileTab({ T, me, onEdit, t, setTweak }) {
|
|
|
2779
2884
|
const pubAvatar = ensRec && ensRec.texts && ensRec.texts.avatar || null;
|
|
2780
2885
|
const punkId = (_a = me.punkId) != null ? _a : pubPunk;
|
|
2781
2886
|
const upAvatar = me.avatarUrl || pubAvatar;
|
|
2782
|
-
|
|
2887
|
+
const canSetAvatar = !!onSetAvatar && !ensRec;
|
|
2888
|
+
return /* @__PURE__ */ React.createElement("div", { style: { flex: 1, overflow: "auto", background: "var(--bg)" } }, /* @__PURE__ */ React.createElement("div", { className: "dk-page", style: { maxWidth: 760, margin: "0 auto", padding: "24px 28px 60px", display: "flex", flexDirection: "column", gap: 24 } }, /* @__PURE__ */ React.createElement("div", { className: "dk-wrap", style: { display: "flex", alignItems: "center", gap: 18, rowGap: 12, padding: "20px 22px", borderRadius: 14, background: "var(--panel)", border: "1px solid var(--line)" } }, /* @__PURE__ */ React.createElement(
|
|
2889
|
+
"div",
|
|
2890
|
+
{
|
|
2891
|
+
style: { position: "relative", cursor: canSetAvatar ? "pointer" : "default" },
|
|
2892
|
+
onClick: canSetAvatar ? () => setAvatarSheet(true) : void 0,
|
|
2893
|
+
title: canSetAvatar ? t && t.lang === "zh" ? "\u66F4\u6362\u5934\u50CF" : "Change avatar" : void 0
|
|
2894
|
+
},
|
|
2895
|
+
upAvatar ? /* @__PURE__ */ React.createElement("img", { src: upAvatar, alt: "", width: 68, height: 68, style: { width: 68, height: 68, borderRadius: 16, objectFit: "cover", background: "var(--panel-2)" } }) : punkId != null ? /* @__PURE__ */ React.createElement(DkPunkAvatar, { id: punkId, size: 68, radius: 16, fallbackSeed: me.userId }) : /* @__PURE__ */ React.createElement(DkIdenticon, { seed: me.userId, size: 68, radius: 16 }),
|
|
2896
|
+
canSetAvatar ? /* @__PURE__ */ React.createElement("span", { style: { position: "absolute", right: -5, bottom: -5, width: 24, height: 24, borderRadius: 999, background: "var(--accent)", border: "3px solid var(--panel)", display: "flex", alignItems: "center", justifyContent: "center", boxSizing: "border-box" } }, /* @__PURE__ */ React.createElement(Icon, { name: "camera", size: 12, stroke: 2.2, color: "#fff" })) : /* @__PURE__ */ React.createElement("span", { style: { position: "absolute", right: -3, bottom: -3, width: 18, height: 18, borderRadius: 999, background: "var(--online)", border: "3px solid var(--panel)" } })
|
|
2897
|
+
), /* @__PURE__ */ React.createElement("div", { style: { flex: 1, minWidth: 0 } }, /* @__PURE__ */ React.createElement("div", { className: "dk-wrap", style: { display: "flex", alignItems: "center", gap: 10, rowGap: 4 } }, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 22, fontWeight: 700, letterSpacing: -0.5, color: "var(--text)", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis", minWidth: 0, maxWidth: "100%" } }, me.name), /* @__PURE__ */ React.createElement(Tag, { tone: "ok" }, "online"), me.isExit && /* @__PURE__ */ React.createElement(Tag, { tone: "warn" }, "exit", me.exitRegion ? ` \xB7 ${me.exitRegion.toUpperCase()}` : "")), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 13, color: "var(--dim)", marginTop: 4, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, me.handle), me.description ? /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12.5, color: "var(--faint)", marginTop: 3 } }, me.description) : null), /* @__PURE__ */ React.createElement(Btn, { icon: "edit", onClick: () => setEditing(true) }, T.editProfile)), /* @__PURE__ */ React.createElement(Card, { label: T.identity }, /* @__PURE__ */ React.createElement(FieldRow, { T, label: T.userId, value: me.userId, copy: true, qr: true, onQr: (v, l) => setQr({ value: v, label: l }) }), /* @__PURE__ */ React.createElement(FieldRow, { T, label: T.carrierAddr, value: me.carrier, copy: true, qr: true, onQr: (v, l) => setQr({ value: v, label: l }) }), /* @__PURE__ */ React.createElement(FieldRow, { T, label: T.netKey, value: me.netKey, copy: true, last: true })), /* @__PURE__ */ React.createElement(DkEnsCard, { T, me, onRecord: setEnsRec }), typeof me.autoAccept === "boolean" && /* @__PURE__ */ React.createElement(Card, { label: T && T.friendsSettings || "Friends" }, /* @__PURE__ */ React.createElement("label", { style: { display: "flex", alignItems: "center", gap: 13, padding: "14px 16px", cursor: "pointer" } }, /* @__PURE__ */ React.createElement(
|
|
2783
2898
|
"input",
|
|
2784
2899
|
{
|
|
2785
2900
|
type: "checkbox",
|
|
@@ -2791,7 +2906,7 @@ function ProfileTab({ T, me, onEdit, t, setTweak }) {
|
|
|
2791
2906
|
},
|
|
2792
2907
|
style: { width: 16, height: 16, accentColor: "var(--accent)" }
|
|
2793
2908
|
}
|
|
2794
|
-
), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }, /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 13.5, fontWeight: 600, color: "var(--text)" } }, T && T.autoAcceptLabel || "Auto-accept friend requests"), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 11.5, color: "var(--dim)", marginTop: 2 } }, T && T.autoAcceptSub || "Off: requests wait in the sidebar for your approval.")))), hostHasKeyBackup && /* @__PURE__ */ React.createElement(DkKeyBackup, { t: t || {} }), t && setTweak && /* @__PURE__ */ React.createElement(DkAlertSettings, { t, setTweak }), t && setTweak && /* @__PURE__ */ React.createElement(DkDisplaySettings, { t, setTweak }), /* @__PURE__ */ React.createElement(Card, { label: T.network }, /* @__PURE__ */ React.createElement(FieldRow, { T, label: T.virtualIp, value: me.ip, copy: true }), /* @__PURE__ */ React.createElement(FieldRow, { T, label: T.wireLabel, value: `${me.wire} \xB7 lossless`, mono: false }), /* @__PURE__ */ React.createElement(DkVersionRow, { T, me }), /* @__PURE__ */ React.createElement(DkUpdateCheck, { T })), /* @__PURE__ */ React.createElement(Card, { label: T.dangerZone }, /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 13, padding: "14px 16px" } }, /* @__PURE__ */ React.createElement("div", { style: { width: 34, height: 34, borderRadius: 8, flexShrink: 0, background: "color-mix(in oklab, var(--danger), transparent 86%)", display: "flex", alignItems: "center", justifyContent: "center", color: "var(--danger)" } }, /* @__PURE__ */ React.createElement(Icon, { name: "trash", size: 17, stroke: 2 })), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }, /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 13.5, fontWeight: 600, color: "var(--danger)" } }, T.deleteNode), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12, color: "var(--faint)", marginTop: 1 } }, T.deleteSub)), /* @__PURE__ */ React.createElement(Btn, { tone: "danger", size: "sm" }, T.delete)))), qr && /* @__PURE__ */ React.createElement(DkQrModal, { T, value: qr.value, label: qr.label, onClose: () => setQr(null) }), editing && /* @__PURE__ */ React.createElement(DkEditModal, { T, me, onClose: () => setEditing(false), onSave: (name, description) => {
|
|
2909
|
+
), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }, /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 13.5, fontWeight: 600, color: "var(--text)" } }, T && T.autoAcceptLabel || "Auto-accept friend requests"), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 11.5, color: "var(--dim)", marginTop: 2 } }, T && T.autoAcceptSub || "Off: requests wait in the sidebar for your approval.")))), hostHasKeyBackup && /* @__PURE__ */ React.createElement(DkKeyBackup, { t: t || {} }), t && setTweak && /* @__PURE__ */ React.createElement(DkAlertSettings, { t, setTweak }), t && setTweak && /* @__PURE__ */ React.createElement(DkDisplaySettings, { t, setTweak }), /* @__PURE__ */ React.createElement(Card, { label: T.network }, /* @__PURE__ */ React.createElement(FieldRow, { T, label: T.virtualIp, value: me.ip, copy: true }), /* @__PURE__ */ React.createElement(FieldRow, { T, label: T.wireLabel, value: `${me.wire} \xB7 lossless`, mono: false }), /* @__PURE__ */ React.createElement(DkVersionRow, { T, me }), /* @__PURE__ */ React.createElement(DkUpdateCheck, { T })), /* @__PURE__ */ React.createElement(Card, { label: T.dangerZone }, /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 13, padding: "14px 16px" } }, /* @__PURE__ */ React.createElement("div", { style: { width: 34, height: 34, borderRadius: 8, flexShrink: 0, background: "color-mix(in oklab, var(--danger), transparent 86%)", display: "flex", alignItems: "center", justifyContent: "center", color: "var(--danger)" } }, /* @__PURE__ */ React.createElement(Icon, { name: "trash", size: 17, stroke: 2 })), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }, /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 13.5, fontWeight: 600, color: "var(--danger)" } }, T.deleteNode), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12, color: "var(--faint)", marginTop: 1 } }, T.deleteSub)), /* @__PURE__ */ React.createElement(Btn, { tone: "danger", size: "sm" }, T.delete)))), qr && /* @__PURE__ */ React.createElement(DkQrModal, { T, value: qr.value, label: qr.label, onClose: () => setQr(null) }), avatarSheet && /* @__PURE__ */ React.createElement(DkAvatarSheet, { T, me, zh: t && t.lang === "zh", onSet: onSetAvatar, onClose: () => setAvatarSheet(false) }), editing && /* @__PURE__ */ React.createElement(DkEditModal, { T, me, onClose: () => setEditing(false), onSave: (name, description) => {
|
|
2795
2910
|
if (onEdit)
|
|
2796
2911
|
onEdit(name, description);
|
|
2797
2912
|
setEditing(false);
|
|
@@ -2800,6 +2915,43 @@ function ProfileTab({ T, me, onEdit, t, setTweak }) {
|
|
|
2800
2915
|
Object.assign(window, { ProfileTab, DkPunkAvatar, DkEnsAvatar, DkPublicProfile });
|
|
2801
2916
|
|
|
2802
2917
|
// src/ui.jsx
|
|
2918
|
+
var DK_PHONE_MQ = "(max-width: 700px)";
|
|
2919
|
+
function useIsPhone() {
|
|
2920
|
+
const mq = React.useMemo(() => typeof window !== "undefined" && window.matchMedia ? window.matchMedia(DK_PHONE_MQ) : null, []);
|
|
2921
|
+
const [phone, setPhone] = React.useState(() => !!(mq && mq.matches));
|
|
2922
|
+
React.useEffect(() => {
|
|
2923
|
+
if (!mq)
|
|
2924
|
+
return void 0;
|
|
2925
|
+
const on = () => setPhone(mq.matches);
|
|
2926
|
+
on();
|
|
2927
|
+
if (mq.addEventListener)
|
|
2928
|
+
mq.addEventListener("change", on);
|
|
2929
|
+
else
|
|
2930
|
+
mq.addListener(on);
|
|
2931
|
+
return () => {
|
|
2932
|
+
if (mq.removeEventListener)
|
|
2933
|
+
mq.removeEventListener("change", on);
|
|
2934
|
+
else
|
|
2935
|
+
mq.removeListener(on);
|
|
2936
|
+
};
|
|
2937
|
+
}, [mq]);
|
|
2938
|
+
return phone;
|
|
2939
|
+
}
|
|
2940
|
+
var DK_PHONE_CSS = `
|
|
2941
|
+
@media ${DK_PHONE_MQ} {
|
|
2942
|
+
.dk-page { padding: 14px 12px 40px !important; gap: 18px !important; }
|
|
2943
|
+
.dk-lbl { width: 78px !important; font-size: 10px !important; letter-spacing: 0.3px !important; }
|
|
2944
|
+
.dk-row { padding: 11px 12px !important; gap: 9px !important; }
|
|
2945
|
+
.dk-wrap { flex-wrap: wrap !important; }
|
|
2946
|
+
.dk-grid2 { grid-template-columns: 1fr 1fr !important; }
|
|
2947
|
+
.dk-grid1 { grid-template-columns: 1fr !important; }
|
|
2948
|
+
.dk-no-phone { display: none !important; }
|
|
2949
|
+
/* iOS zooms the page into any focused field under 16px and never zooms back. */
|
|
2950
|
+
input:not([type=checkbox]):not([type=radio]), textarea, select { font-size: 16px !important; }
|
|
2951
|
+
}`;
|
|
2952
|
+
function DkPhoneStyles() {
|
|
2953
|
+
return /* @__PURE__ */ React.createElement("style", null, DK_PHONE_CSS);
|
|
2954
|
+
}
|
|
2803
2955
|
function DkIdenticon({ seed, size = 30, radius = 7 }) {
|
|
2804
2956
|
const { cells, hue } = dkIdenticon(seed);
|
|
2805
2957
|
const cell = size / 5;
|
|
@@ -2973,7 +3125,7 @@ function Btn({ children, icon, tone = "ghost", onClick, size = "md", title, styl
|
|
|
2973
3125
|
function Section({ label, count, trailing, style }) {
|
|
2974
3126
|
return /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 8, padding: "0 2px", ...style } }, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 11, fontWeight: 700, letterSpacing: 1, textTransform: "uppercase", color: "var(--faint)" } }, label), count != null && /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 11, fontWeight: 700, color: "var(--faint)" } }, count), /* @__PURE__ */ React.createElement("div", { style: { flex: 1, height: 1, background: "var(--line)" } }), trailing);
|
|
2975
3127
|
}
|
|
2976
|
-
Object.assign(window, { DkIdenticon, DkBot, DkAvatar, DkImgAvatar, StatusDot, Mono, shortKey, RouteTag, Tag, Unread, Btn, Section });
|
|
3128
|
+
Object.assign(window, { useIsPhone, DkPhoneStyles, DkIdenticon, DkBot, DkAvatar, DkImgAvatar, StatusDot, Mono, shortKey, RouteTag, Tag, Unread, Btn, Section });
|
|
2977
3129
|
|
|
2978
3130
|
// src/apps.jsx
|
|
2979
3131
|
var APPS = [
|
|
@@ -3823,9 +3975,9 @@ function DiscoverTab({ T, kind, peers, meId, onAdd, onOpenChat }) {
|
|
|
3823
3975
|
value: q,
|
|
3824
3976
|
onChange: (e) => setQ(e.target.value),
|
|
3825
3977
|
placeholder: T.dirSearch || "search name / userid",
|
|
3826
|
-
style: { flex: 1, background: "transparent", border: "none", outline: "none", color: "var(--text)", fontFamily: "var(--ui)", fontSize: 13 }
|
|
3978
|
+
style: { flex: 1, minWidth: 0, background: "transparent", border: "none", outline: "none", color: "var(--text)", fontFamily: "var(--ui)", fontSize: 13 }
|
|
3827
3979
|
}
|
|
3828
|
-
), kind === "bridge" && (meta == null ? void 0 : meta.bridge) && /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 11.5, color: "var(--faint)" } }, String(meta.bridge).replace(/^https?:\/\//, ""), meta.online ? ` \xB7 ${meta.online} online` : ""), list && /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 11.5, color: "var(--faint)" } }, shown.length, "/", list.length)), /* @__PURE__ */ React.createElement("div", { style: { flex: 1, minHeight: 0, overflowY: "auto" } }, err ? /* @__PURE__ */ React.createElement("div", { style: { padding: 22, fontFamily: "var(--ui)", fontSize: 12.5, color: "var(--faint)" } }, (T.dirError || "Could not load the directory:") + " " + err) : !list ? /* @__PURE__ */ React.createElement("div", { style: { padding: 22, fontFamily: "var(--ui)", fontSize: 12.5, color: "var(--faint)" } }, T.dirLoading || "loading\u2026") : shown.length === 0 ? /* @__PURE__ */ React.createElement("div", { style: { padding: 22, fontFamily: "var(--ui)", fontSize: 12.5, color: "var(--faint)" } }, kind === "bridge" && (meta == null ? void 0 : meta.pending) ? T.bridgePending || "Not verified on this bridge yet \u2014 it may still be running the older protocol." : kind === "bridge" ? T.bridgeEmpty || "Nobody else is listed on this bridge yet." : T.dirEmpty || "nobody here") : shown.map((p) => /* @__PURE__ */ React.createElement(
|
|
3980
|
+
), kind === "bridge" && (meta == null ? void 0 : meta.bridge) && /* @__PURE__ */ React.createElement("span", { className: "dk-no-phone", style: { fontFamily: "var(--mono)", fontSize: 11.5, color: "var(--faint)", whiteSpace: "nowrap" } }, String(meta.bridge).replace(/^https?:\/\//, ""), meta.online ? ` \xB7 ${meta.online} online` : ""), list && /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 11.5, color: "var(--faint)", whiteSpace: "nowrap", flexShrink: 0 } }, shown.length, "/", list.length)), /* @__PURE__ */ React.createElement("div", { style: { flex: 1, minHeight: 0, overflowY: "auto" } }, err ? /* @__PURE__ */ React.createElement("div", { style: { padding: 22, fontFamily: "var(--ui)", fontSize: 12.5, color: "var(--faint)" } }, (T.dirError || "Could not load the directory:") + " " + err) : !list ? /* @__PURE__ */ React.createElement("div", { style: { padding: 22, fontFamily: "var(--ui)", fontSize: 12.5, color: "var(--faint)" } }, T.dirLoading || "loading\u2026") : shown.length === 0 ? /* @__PURE__ */ React.createElement("div", { style: { padding: 22, fontFamily: "var(--ui)", fontSize: 12.5, color: "var(--faint)" } }, kind === "bridge" && (meta == null ? void 0 : meta.pending) ? T.bridgePending || "Not verified on this bridge yet \u2014 it may still be running the older protocol." : kind === "bridge" ? T.bridgeEmpty || "Nobody else is listed on this bridge yet." : T.dirEmpty || "nobody here") : shown.map((p) => /* @__PURE__ */ React.createElement(
|
|
3829
3981
|
DkDirRow,
|
|
3830
3982
|
{
|
|
3831
3983
|
key: p.userid,
|
|
@@ -4218,14 +4370,20 @@ Object.assign(window, { useRtcFileController, RtcFileInbox });
|
|
|
4218
4370
|
|
|
4219
4371
|
// src/network.jsx
|
|
4220
4372
|
function StatTile({ label, value, sub, tone }) {
|
|
4221
|
-
return /* @__PURE__ */ React.createElement("div", { style: { flex: 1, minWidth: 0, padding: "13px 15px", borderRadius: 10, background: "var(--panel)", border: "1px solid var(--line)" } }, /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 10.5, fontWeight: 700, letterSpacing: 1, textTransform: "uppercase", color: "var(--faint)" } }, label), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 21, fontWeight: 700, letterSpacing: -0.5, marginTop: 5, color: tone || "var(--text)" } }, value), sub && /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 11, color: "var(--dim)", marginTop: 2 } }, sub));
|
|
4373
|
+
return /* @__PURE__ */ React.createElement("div", { style: { flex: 1, minWidth: 0, padding: "13px 15px", borderRadius: 10, background: "var(--panel)", border: "1px solid var(--line)" } }, /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 10.5, fontWeight: 700, letterSpacing: 1, textTransform: "uppercase", color: "var(--faint)", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" } }, label), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 21, fontWeight: 700, letterSpacing: -0.5, marginTop: 5, color: tone || "var(--text)", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" } }, value), sub && /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 11, color: "var(--dim)", marginTop: 2 } }, sub));
|
|
4222
4374
|
}
|
|
4223
4375
|
function MyNode({ T, me, activeExit, peers, reqCount = 0 }) {
|
|
4224
4376
|
const online = peers.filter((p) => p.online).length;
|
|
4225
4377
|
const direct = peers.filter((p) => p.online && p.via === "direct").length;
|
|
4226
|
-
return /* @__PURE__ */ React.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 12 } }, /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 14, padding: "16px 18px", borderRadius: 12, background: "var(--panel)", border: "1px solid var(--line)" } }, /* @__PURE__ */ React.createElement(
|
|
4378
|
+
return /* @__PURE__ */ React.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 12 } }, /* @__PURE__ */ React.createElement("div", { className: "dk-wrap", style: { display: "flex", alignItems: "center", gap: 14, padding: "16px 18px", borderRadius: 12, background: "var(--panel)", border: "1px solid var(--line)" } }, /* @__PURE__ */ React.createElement(DkAvatar, { peer: { ...me, id: me.userId, agent: false }, size: 46, radius: 11 }), /* @__PURE__ */ React.createElement("div", { style: { minWidth: 0, flex: 1 } }, /* @__PURE__ */ React.createElement("div", { className: "dk-wrap", style: { display: "flex", alignItems: "center", gap: 9 } }, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 17, fontWeight: 700, color: "var(--text)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", minWidth: 0 } }, me.name), /* @__PURE__ */ React.createElement(Tag, { tone: me.online ? "ok" : "off" }, me.online ? "online" : "offline"), /* @__PURE__ */ React.createElement(Tag, { tone: "accent" }, me.channel), me.isExit && /* @__PURE__ */ React.createElement(Tag, { tone: "warn" }, "exit", me.exitRegion ? ` \xB7 ${me.exitRegion.toUpperCase()}` : "")), /* @__PURE__ */ React.createElement("div", { className: "dk-wrap", style: { display: "flex", alignItems: "center", gap: 8, marginTop: 5, rowGap: 2 } }, me.ip ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 11.5, color: "var(--faint)" } }, T.myIp), /* @__PURE__ */ React.createElement(Mono, { size: 13, copy: me.ip }, me.ip), /* @__PURE__ */ React.createElement("span", { style: { color: "var(--line)" } }, "\xB7")) : null, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 11.5, color: "var(--faint)" } }, "wire ", me.wire), /* @__PURE__ */ React.createElement("span", { style: { color: "var(--line)" } }, "\xB7"), /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 11.5, color: "var(--faint)" } }, "lan ", me.lanVer, " / peer ", me.peerVer))), /* @__PURE__ */ React.createElement(Btn, { icon: "copy", onClick: () => dkCopy(me.carrier) }, T.copyAddr)), /* @__PURE__ */ React.createElement("div", { className: "dk-grid2", style: { display: "grid", gridTemplateColumns: "repeat(4, minmax(0, 1fr))", gap: 12 } }, /* @__PURE__ */ React.createElement(StatTile, { label: T.peersOnline, value: `${online}/${peers.length}`, sub: `${direct} ${T.direct}`, tone: "var(--online)" }), /* @__PURE__ */ React.createElement(StatTile, { label: T.activeEgress, value: activeExit ? activeExit : T.directEgress, sub: activeExit ? T.routing : T.noProxy, tone: activeExit ? "var(--warn)" : "var(--text)" }), /* @__PURE__ */ React.createElement(StatTile, { label: T.wireLabel, value: me.wire, sub: `${me.channel} \xB7 ${T.lossless}` }), /* @__PURE__ */ React.createElement(StatTile, { label: T.reqs, value: String(reqCount), sub: T.pending, tone: reqCount ? "var(--accent)" : "var(--text)" })));
|
|
4227
4379
|
}
|
|
4228
4380
|
function PeerTable({ T, peers, onOpenChat }) {
|
|
4381
|
+
const phone = useIsPhone();
|
|
4382
|
+
if (!peers.length)
|
|
4383
|
+
return null;
|
|
4384
|
+
if (phone) {
|
|
4385
|
+
return /* @__PURE__ */ React.createElement("div", { style: { borderRadius: 11, border: "1px solid var(--line)", overflow: "hidden", background: "var(--panel)" } }, peers.map((p, i) => /* @__PURE__ */ React.createElement("div", { key: p.id, onClick: () => onOpenChat(p.id), style: { display: "flex", alignItems: "center", gap: 10, padding: "10px 12px", cursor: "pointer", borderBottom: i < peers.length - 1 ? "1px solid var(--line)" : "none" } }, /* @__PURE__ */ React.createElement(DkAvatar, { peer: p, size: 30, radius: 7 }), /* @__PURE__ */ React.createElement("div", { style: { flex: 1, minWidth: 0, display: "flex", flexDirection: "column", gap: 3 } }, /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 6, minWidth: 0 } }, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: p.alias ? "var(--ui)" : "var(--mono)", fontSize: 13.5, fontWeight: 600, color: "var(--text)", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis", minWidth: 0 } }, p.alias || shortKey(p.userId, 8, 5)), p.agent && /* @__PURE__ */ React.createElement(Icon, { name: "bot", size: 12, color: "var(--faint)", stroke: 2 })), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 6, minWidth: 0 } }, /* @__PURE__ */ React.createElement(RouteTag, { peer: p }), p.ip ? /* @__PURE__ */ React.createElement(Mono, { size: 11.5, dim: true, copy: p.ip }, p.ip) : null)), /* @__PURE__ */ React.createElement(Icon, { name: "chevronRight", size: 16, color: "var(--faint)", stroke: 2 }))));
|
|
4386
|
+
}
|
|
4229
4387
|
return /* @__PURE__ */ React.createElement("div", { style: { borderRadius: 11, border: "1px solid var(--line)", overflow: "hidden", background: "var(--panel)" } }, /* @__PURE__ */ React.createElement("div", { style: { display: "grid", gridTemplateColumns: "1.4fr 1fr 1fr 0.8fr 90px", gap: 0, padding: "9px 16px", borderBottom: "1px solid var(--line)", background: "var(--panel-2)" } }, [T.colPeer, T.colVip, T.colPath, T.colWire, ""].map((h, i) => /* @__PURE__ */ React.createElement("span", { key: i, style: { fontFamily: "var(--mono)", fontSize: 10.5, fontWeight: 700, letterSpacing: 0.8, textTransform: "uppercase", color: "var(--faint)", textAlign: i === 4 ? "right" : "left" } }, h))), peers.map((p, i) => /* @__PURE__ */ React.createElement("div", { key: p.id, style: {
|
|
4230
4388
|
display: "grid",
|
|
4231
4389
|
gridTemplateColumns: "1.4fr 1fr 1fr 0.8fr 90px",
|
|
@@ -4243,7 +4401,7 @@ function ExitCard({ T, region, activeExit, onSetExit }) {
|
|
|
4243
4401
|
})()), /* @__PURE__ */ React.createElement("div", null, region.nodes.map((n, i) => {
|
|
4244
4402
|
const active = n.ip === activeExit;
|
|
4245
4403
|
const stuck = n.online && !n.reachable;
|
|
4246
|
-
return /* @__PURE__ */ React.createElement("div", { key: n.ip, style: { display: "flex", alignItems: "center", gap: 12, padding: "10px 14px", borderBottom: i < region.nodes.length - 1 ? "1px solid var(--line)" : "none", background: active ? "color-mix(in oklab, var(--accent), transparent 92%)" : "transparent" } }, /* @__PURE__ */ React.createElement(StatusDot, { online: n.reachable }), /* @__PURE__ */ React.createElement(Mono, { size: 13, copy: n.ip }, n.ip), /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 11, color: "var(--faint)" } }, n.host), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }), n.reachable ? /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 11.5, color: n.ping > 200 ? "var(--warn)" : "var(--dim)" } }, n.ping, "ms") : stuck ? /* @__PURE__ */ React.createElement(Tag, { tone: "warn", title: "Announces online but IP won't pass \u2014 session desync or NAT-blocked" }, "online \xB7 no route") : /* @__PURE__ */ React.createElement(Tag, { tone: "off" }, "offline"), active ? /* @__PURE__ */ React.createElement(Btn, { tone: "ok", icon: "check", size: "sm", onClick: () => onSetExit(null) }, T.routing) : /* @__PURE__ */ React.createElement(Btn, { size: "sm", icon: "route", onClick: () => n.reachable && onSetExit(n.ip), style: { opacity: n.reachable ? 1 : 0.4 } }, T.routeThru));
|
|
4404
|
+
return /* @__PURE__ */ React.createElement("div", { key: n.ip, className: "dk-wrap", style: { display: "flex", alignItems: "center", gap: 12, rowGap: 6, padding: "10px 14px", borderBottom: i < region.nodes.length - 1 ? "1px solid var(--line)" : "none", background: active ? "color-mix(in oklab, var(--accent), transparent 92%)" : "transparent" } }, /* @__PURE__ */ React.createElement(StatusDot, { online: n.reachable }), /* @__PURE__ */ React.createElement(Mono, { size: 13, copy: n.ip }, n.ip), /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 11, color: "var(--faint)" } }, n.host), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }), n.reachable ? /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 11.5, color: n.ping > 200 ? "var(--warn)" : "var(--dim)" } }, n.ping, "ms") : stuck ? /* @__PURE__ */ React.createElement(Tag, { tone: "warn", title: "Announces online but IP won't pass \u2014 session desync or NAT-blocked" }, "online \xB7 no route") : /* @__PURE__ */ React.createElement(Tag, { tone: "off" }, "offline"), active ? /* @__PURE__ */ React.createElement(Btn, { tone: "ok", icon: "check", size: "sm", onClick: () => onSetExit(null) }, T.routing) : /* @__PURE__ */ React.createElement(Btn, { size: "sm", icon: "route", onClick: () => n.reachable && onSetExit(n.ip), style: { opacity: n.reachable ? 1 : 0.4 } }, T.routeThru));
|
|
4247
4405
|
})));
|
|
4248
4406
|
}
|
|
4249
4407
|
function LanEnableCard({ backend, onArm, onCancel }) {
|
|
@@ -4349,9 +4507,10 @@ function LanEnableCard({ backend, onArm, onCancel }) {
|
|
|
4349
4507
|
}
|
|
4350
4508
|
function NetworkTab({ T, me, peers, exits, activeExit, reqCount, onSetExit, onOpenChat, backend, onArmLan, onCancelLan }) {
|
|
4351
4509
|
if (backend && backend.switchable && !backend.hasVirtualLan) {
|
|
4352
|
-
return /* @__PURE__ */ React.createElement("div", { style: { flex: 1, overflow: "auto", background: "var(--bg)" } }, /* @__PURE__ */ React.createElement("div", { style: { maxWidth: 1040, margin: "0 auto", padding: "24px 28px 60px", display: "flex", flexDirection: "column", gap: 26 } }, /* @__PURE__ */ React.createElement(MyNode, { T, me, activeExit, peers, reqCount }), /* @__PURE__ */ React.createElement(LanEnableCard, { backend, onArm: onArmLan, onCancel: onCancelLan })));
|
|
4510
|
+
return /* @__PURE__ */ React.createElement("div", { style: { flex: 1, overflow: "auto", background: "var(--bg)" } }, /* @__PURE__ */ React.createElement("div", { className: "dk-page", style: { maxWidth: 1040, margin: "0 auto", padding: "24px 28px 60px", display: "flex", flexDirection: "column", gap: 26 } }, /* @__PURE__ */ React.createElement(MyNode, { T, me, activeExit, peers, reqCount }), /* @__PURE__ */ React.createElement(LanEnableCard, { backend, onArm: onArmLan, onCancel: onCancelLan })));
|
|
4353
4511
|
}
|
|
4354
|
-
|
|
4512
|
+
const lanless = !!backend && backend.hasVirtualLan === false;
|
|
4513
|
+
return /* @__PURE__ */ React.createElement("div", { style: { flex: 1, overflow: "auto", background: "var(--bg)" } }, /* @__PURE__ */ React.createElement("div", { className: "dk-page", style: { maxWidth: 1040, margin: "0 auto", padding: "24px 28px 60px", display: "flex", flexDirection: "column", gap: 26 } }, /* @__PURE__ */ React.createElement(MyNode, { T, me, activeExit, peers, reqCount }), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 12 } }, /* @__PURE__ */ React.createElement(Section, { label: T.peerRouting, count: peers.length }), /* @__PURE__ */ React.createElement(PeerTable, { T, peers, onOpenChat }), !peers.length && /* @__PURE__ */ React.createElement("div", { style: { padding: "14px 16px", borderRadius: 11, border: "1px dashed var(--line)", fontFamily: "var(--ui)", fontSize: 12.5, color: "var(--faint)" } }, T.noPeersYet || "No friends yet \u2014 add one from Chat and it shows up here with its route.")), lanless ? null : /* @__PURE__ */ React.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 12 } }, /* @__PURE__ */ React.createElement(Section, { label: T.exitNodes, trailing: /* @__PURE__ */ React.createElement("div", { style: { display: "flex", gap: 6 } }, /* @__PURE__ */ React.createElement(Btn, { icon: "plus", size: "sm" }, T.addExit)) }), activeExit && /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 10, padding: "11px 14px", borderRadius: 10, background: "color-mix(in oklab, var(--warn), transparent 90%)", border: "1px solid color-mix(in oklab, var(--warn), transparent 70%)" } }, /* @__PURE__ */ React.createElement(Icon, { name: "route", size: 17, color: "var(--warn)", stroke: 2 }), /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 12.5, color: "var(--text)" } }, T.egressVia), /* @__PURE__ */ React.createElement(Mono, { size: 13, copy: activeExit }, activeExit), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }), /* @__PURE__ */ React.createElement(Btn, { tone: "danger", icon: "unlink", size: "sm", onClick: () => onSetExit(null) }, T.stopRouting)), /* @__PURE__ */ React.createElement("div", { className: "dk-grid1", style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 } }, exits.map((r) => /* @__PURE__ */ React.createElement(ExitCard, { key: r.region, T, region: r, activeExit, onSetExit }))))));
|
|
4355
4514
|
}
|
|
4356
4515
|
Object.assign(window, { NetworkTab });
|
|
4357
4516
|
|
|
@@ -4471,8 +4630,10 @@ function dkFlashStop() {
|
|
|
4471
4630
|
function dkLooking() {
|
|
4472
4631
|
return document.visibilityState === "visible" && document.hasFocus();
|
|
4473
4632
|
}
|
|
4474
|
-
function useIncomingAlerts({ T, peers, requests, loaded, activeId, sound = true, flash = true }) {
|
|
4633
|
+
function useIncomingAlerts({ T, peers, requests, loaded, activeId, sound = true, flash = true, muted }) {
|
|
4475
4634
|
const prev = React.useRef(null);
|
|
4635
|
+
const mutedRef = React.useRef(/* @__PURE__ */ new Set());
|
|
4636
|
+
mutedRef.current = new Set(muted || []);
|
|
4476
4637
|
React.useEffect(() => dkUnlockAudioOnGesture(), []);
|
|
4477
4638
|
React.useEffect(() => {
|
|
4478
4639
|
if (!flash)
|
|
@@ -4506,7 +4667,7 @@ function useIncomingAlerts({ T, peers, requests, loaded, activeId, sound = true,
|
|
|
4506
4667
|
prev.current = { byId, reqIds };
|
|
4507
4668
|
if (!was)
|
|
4508
4669
|
return;
|
|
4509
|
-
let msgs = 0, elsewhere = 0, reqs = 0;
|
|
4670
|
+
let msgs = 0, elsewhere = 0, reqs = 0, loud = 0, loudElsewhere = 0;
|
|
4510
4671
|
for (const [id, cur] of byId) {
|
|
4511
4672
|
if (cur.dir !== "in" || !cur.ts)
|
|
4512
4673
|
continue;
|
|
@@ -4516,6 +4677,11 @@ function useIncomingAlerts({ T, peers, requests, loaded, activeId, sound = true,
|
|
|
4516
4677
|
msgs += 1;
|
|
4517
4678
|
if (id !== activeId)
|
|
4518
4679
|
elsewhere += 1;
|
|
4680
|
+
if (!mutedRef.current.has(id)) {
|
|
4681
|
+
loud += 1;
|
|
4682
|
+
if (id !== activeId)
|
|
4683
|
+
loudElsewhere += 1;
|
|
4684
|
+
}
|
|
4519
4685
|
}
|
|
4520
4686
|
for (const id of reqIds)
|
|
4521
4687
|
if (!was.reqIds.has(id))
|
|
@@ -4523,8 +4689,8 @@ function useIncomingAlerts({ T, peers, requests, loaded, activeId, sound = true,
|
|
|
4523
4689
|
if (!msgs && !reqs)
|
|
4524
4690
|
return;
|
|
4525
4691
|
const looking = dkLooking();
|
|
4526
|
-
if (sound && !(looking && !reqs && !
|
|
4527
|
-
dkChime(reqs && !
|
|
4692
|
+
if (sound && (reqs || loud) && !(looking && !reqs && !loudElsewhere))
|
|
4693
|
+
dkChime(reqs && !loud ? "request" : "message");
|
|
4528
4694
|
if (flash && !looking) {
|
|
4529
4695
|
const label = msgs ? T && T.alertMsg || "New message" : T && T.alertReq || "New friend request";
|
|
4530
4696
|
dkFlashStart(label, msgs + reqs);
|
|
@@ -4665,9 +4831,14 @@ var STR = {
|
|
|
4665
4831
|
profile: "Profile",
|
|
4666
4832
|
alertMsg: "New message",
|
|
4667
4833
|
alertReq: "New friend request",
|
|
4834
|
+
muteSound: "Mute sound for this chat",
|
|
4835
|
+
unmuteSound: "Turn sound back on for this chat",
|
|
4836
|
+
mutedTag: "sound muted",
|
|
4668
4837
|
recommended: "Discover",
|
|
4669
4838
|
registered: "Names",
|
|
4670
4839
|
here: "Here",
|
|
4840
|
+
people: "People",
|
|
4841
|
+
back: "Back",
|
|
4671
4842
|
dirAdd: "Add",
|
|
4672
4843
|
dirSent: "requested",
|
|
4673
4844
|
dirOpenChat: "chat",
|
|
@@ -4847,9 +5018,14 @@ var STR = {
|
|
|
4847
5018
|
profile: "\u6211\u7684",
|
|
4848
5019
|
alertMsg: "\u65B0\u6D88\u606F",
|
|
4849
5020
|
alertReq: "\u65B0\u7684\u597D\u53CB\u8BF7\u6C42",
|
|
5021
|
+
muteSound: "\u5173\u95ED\u6B64\u4F1A\u8BDD\u7684\u63D0\u793A\u97F3",
|
|
5022
|
+
unmuteSound: "\u6062\u590D\u6B64\u4F1A\u8BDD\u7684\u63D0\u793A\u97F3",
|
|
5023
|
+
mutedTag: "\u5DF2\u9759\u97F3",
|
|
4850
5024
|
recommended: "\u63A8\u8350",
|
|
4851
5025
|
registered: "\u540D\u5F55",
|
|
4852
5026
|
here: "\u5728\u573A",
|
|
5027
|
+
people: "\u53D1\u73B0",
|
|
5028
|
+
back: "\u8FD4\u56DE",
|
|
4853
5029
|
dirAdd: "\u52A0\u597D\u53CB",
|
|
4854
5030
|
dirSent: "\u5DF2\u8BF7\u6C42",
|
|
4855
5031
|
dirOpenChat: "\u4F1A\u8BDD",
|
|
@@ -5040,6 +5216,24 @@ function RailBtn({ icon, label, active, soon, onClick }) {
|
|
|
5040
5216
|
gap: 3
|
|
5041
5217
|
} }, /* @__PURE__ */ React.createElement(Icon, { name: icon, size: 20, stroke: active ? 2.1 : 1.8, color: active ? "var(--accent)" : "var(--dim)" }), /* @__PURE__ */ React.createElement("span", { style: { fontSize: 9, fontWeight: 600, letterSpacing: 0.2, color: active ? "var(--accent)" : "var(--faint)" } }, label), soon && /* @__PURE__ */ React.createElement("span", { style: { position: "absolute", top: 5, right: 6, width: 6, height: 6, borderRadius: 999, background: "var(--warn)" } }));
|
|
5042
5218
|
}
|
|
5219
|
+
function TabBarBtn({ icon, label, active, badge, onClick }) {
|
|
5220
|
+
return /* @__PURE__ */ React.createElement("button", { onClick, style: {
|
|
5221
|
+
flex: 1,
|
|
5222
|
+
minWidth: 0,
|
|
5223
|
+
height: 54,
|
|
5224
|
+
border: "none",
|
|
5225
|
+
background: "transparent",
|
|
5226
|
+
cursor: "pointer",
|
|
5227
|
+
position: "relative",
|
|
5228
|
+
display: "flex",
|
|
5229
|
+
flexDirection: "column",
|
|
5230
|
+
alignItems: "center",
|
|
5231
|
+
justifyContent: "center",
|
|
5232
|
+
gap: 3,
|
|
5233
|
+
fontFamily: "var(--mono)"
|
|
5234
|
+
} }, /* @__PURE__ */ React.createElement(Icon, { name: icon, size: 22, stroke: active ? 2.1 : 1.8, color: active ? "var(--accent)" : "var(--dim)" }), /* @__PURE__ */ React.createElement("span", { style: { fontSize: 10, fontWeight: 600, color: active ? "var(--accent)" : "var(--faint)", whiteSpace: "nowrap" } }, label), badge ? /* @__PURE__ */ React.createElement("span", { style: { position: "absolute", top: 4, left: "50%", marginLeft: 6 } }, /* @__PURE__ */ React.createElement(Unread, { n: badge })) : null);
|
|
5235
|
+
}
|
|
5236
|
+
var DK_PEOPLE_TABS = ["here", "recommended", "registered"];
|
|
5043
5237
|
function DkUpdateModal({ T, info, onClose }) {
|
|
5044
5238
|
const [phase, setPhase] = React.useState("idle");
|
|
5045
5239
|
const [err, setErr] = React.useState(null);
|
|
@@ -5245,6 +5439,8 @@ function DkApp() {
|
|
|
5245
5439
|
const T = STR[t.lang] || STR.en;
|
|
5246
5440
|
const vars = dkTheme(t.theme, t.accent);
|
|
5247
5441
|
const rowPad = t.density === "comfortable" ? "11px 12px" : "7px 10px";
|
|
5442
|
+
const mutedIds = Array.isArray(t.alertMuted) ? t.alertMuted : [];
|
|
5443
|
+
const onToggleMute = (id) => setTweak("alertMuted", mutedIds.includes(id) ? mutedIds.filter((x) => x !== id) : mutedIds.concat(id));
|
|
5248
5444
|
useIncomingAlerts({
|
|
5249
5445
|
T,
|
|
5250
5446
|
peers,
|
|
@@ -5252,7 +5448,8 @@ function DkApp() {
|
|
|
5252
5448
|
loaded: data.loaded,
|
|
5253
5449
|
activeId,
|
|
5254
5450
|
sound: t.alertSound !== false,
|
|
5255
|
-
flash: t.alertFlash !== false
|
|
5451
|
+
flash: t.alertFlash !== false,
|
|
5452
|
+
muted: mutedIds
|
|
5256
5453
|
});
|
|
5257
5454
|
const callCtl = useCallController(me.userId, (peerId, video, direction) => {
|
|
5258
5455
|
const text = `WebRTC ${video ? "video" : "audio"} call: ${direction}`;
|
|
@@ -5274,10 +5471,34 @@ function DkApp() {
|
|
|
5274
5471
|
data.refresh();
|
|
5275
5472
|
});
|
|
5276
5473
|
});
|
|
5474
|
+
const phone = useIsPhone();
|
|
5277
5475
|
React.useEffect(() => {
|
|
5278
|
-
if (!activeId && peers.length)
|
|
5476
|
+
if (!phone && !activeId && peers.length)
|
|
5279
5477
|
setActiveId(peers[0].id);
|
|
5280
|
-
}, [peers, activeId]);
|
|
5478
|
+
}, [peers, activeId, phone]);
|
|
5479
|
+
React.useEffect(() => {
|
|
5480
|
+
if (!phone)
|
|
5481
|
+
return void 0;
|
|
5482
|
+
const onPop = () => setActiveId(null);
|
|
5483
|
+
window.addEventListener("popstate", onPop);
|
|
5484
|
+
return () => window.removeEventListener("popstate", onPop);
|
|
5485
|
+
}, [phone]);
|
|
5486
|
+
const openConversation = (id) => {
|
|
5487
|
+
setActiveId(id);
|
|
5488
|
+
if (phone && id) {
|
|
5489
|
+
try {
|
|
5490
|
+
window.history.pushState({ dkConv: id }, "");
|
|
5491
|
+
} catch (e) {
|
|
5492
|
+
}
|
|
5493
|
+
}
|
|
5494
|
+
};
|
|
5495
|
+
const closeConversation = () => {
|
|
5496
|
+
const st = window.history.state;
|
|
5497
|
+
if (st && st.dkConv)
|
|
5498
|
+
window.history.back();
|
|
5499
|
+
else
|
|
5500
|
+
setActiveId(null);
|
|
5501
|
+
};
|
|
5281
5502
|
React.useEffect(() => {
|
|
5282
5503
|
if (!activeId)
|
|
5283
5504
|
return;
|
|
@@ -5289,7 +5510,7 @@ function DkApp() {
|
|
|
5289
5510
|
}, 2500);
|
|
5290
5511
|
return () => clearInterval(iv);
|
|
5291
5512
|
}, [activeId]);
|
|
5292
|
-
const onSelect = (id) =>
|
|
5513
|
+
const onSelect = (id) => openConversation(id);
|
|
5293
5514
|
const onAct = (id, kind) => {
|
|
5294
5515
|
const r = requests.find((x) => x.id === id);
|
|
5295
5516
|
const uid = r && r.userid || id;
|
|
@@ -5385,8 +5606,8 @@ function DkApp() {
|
|
|
5385
5606
|
const onSetExit = () => {
|
|
5386
5607
|
};
|
|
5387
5608
|
const onOpenChat = (id) => {
|
|
5388
|
-
setActiveId(id);
|
|
5389
5609
|
setTab("chat");
|
|
5610
|
+
openConversation(id);
|
|
5390
5611
|
};
|
|
5391
5612
|
const onOpenNet = () => setTab("network");
|
|
5392
5613
|
const nav = [
|
|
@@ -5397,14 +5618,44 @@ function DkApp() {
|
|
|
5397
5618
|
{ id: "network", icon: "network", label: T.network },
|
|
5398
5619
|
{ id: "apps", icon: "grid", label: T.apps }
|
|
5399
5620
|
];
|
|
5400
|
-
|
|
5621
|
+
const peopleTab = DK_PEOPLE_TABS.includes(tab);
|
|
5622
|
+
const lastPeople = React.useRef("here");
|
|
5623
|
+
if (peopleTab)
|
|
5624
|
+
lastPeople.current = tab;
|
|
5625
|
+
const unreadTotal = (peers || []).reduce((n, p) => n + (Number(p.unread) || 0), 0) + (requests || []).length;
|
|
5626
|
+
const phoneNav = [
|
|
5627
|
+
{ id: "chat", icon: "message", label: T.chat, active: tab === "chat", badge: unreadTotal, go: () => setTab("chat") },
|
|
5628
|
+
{ id: "people", icon: "users", label: T.people, active: peopleTab, go: () => setTab(lastPeople.current) },
|
|
5629
|
+
{ id: "network", icon: "network", label: T.network, active: tab === "network", go: () => setTab("network") },
|
|
5630
|
+
{ id: "apps", icon: "grid", label: T.apps, active: tab === "apps", go: () => setTab("apps") },
|
|
5631
|
+
{ id: "profile", icon: "userRound", label: T.profile, active: tab === "profile", go: () => setTab("profile") }
|
|
5632
|
+
];
|
|
5633
|
+
const inConversation = phone && tab === "chat" && !!activeId && (peers || []).some((p) => p.id === activeId);
|
|
5634
|
+
return /* @__PURE__ */ React.createElement("div", { style: { ...vars, "--row-pad": rowPad, position: "fixed", inset: 0, display: "flex", flexDirection: phone ? "column" : "row", background: "var(--bg)", color: "var(--text)", fontFamily: "var(--ui)" } }, /* @__PURE__ */ React.createElement(DkPhoneStyles, null), !phone && /* @__PURE__ */ React.createElement("div", { style: { width: 68, flexShrink: 0, borderRight: "1px solid var(--line)", background: "var(--rail)", display: "flex", flexDirection: "column", alignItems: "center", padding: "14px 0", gap: 8 } }, /* @__PURE__ */ React.createElement("div", { style: { width: 38, height: 38, borderRadius: 10, background: "var(--accent)", display: "flex", alignItems: "center", justifyContent: "center", marginBottom: 8 } }, /* @__PURE__ */ React.createElement(Icon, { name: "terminal", size: 20, color: "#fff", stroke: 2.2 })), nav.map((n) => /* @__PURE__ */ React.createElement(RailBtn, { key: n.id, icon: n.icon, label: n.label, active: tab === n.id, soon: n.soon, onClick: () => setTab(n.id) })), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }), /* @__PURE__ */ React.createElement("button", { onClick: () => setTab("profile"), title: T.profile, style: {
|
|
5401
5635
|
padding: 4,
|
|
5402
5636
|
borderRadius: 12,
|
|
5403
5637
|
cursor: "pointer",
|
|
5404
5638
|
display: "flex",
|
|
5405
5639
|
border: "1px solid " + (tab === "profile" ? "var(--line)" : "transparent"),
|
|
5406
5640
|
background: tab === "profile" ? "var(--panel-2)" : "transparent"
|
|
5407
|
-
} }, /* @__PURE__ */ React.createElement(DkAvatar, { peer: { ...me, id: me.userId, agent: false }, size: 36, radius: 9 }))), /* @__PURE__ */ React.createElement("div", { style: { flex: 1, minWidth: 0, minHeight: 0, display: "flex", flexDirection: "column" } }, /* @__PURE__ */ React.createElement(DkBrowserNotice, { lang: t.lang }), /* @__PURE__ */ React.createElement("div", { style: { height: 46, flexShrink: 0, borderBottom: "1px solid var(--line)", background: "var(--panel)", display: "flex", alignItems: "center", gap: 12,
|
|
5641
|
+
} }, /* @__PURE__ */ React.createElement(DkAvatar, { peer: { ...me, id: me.userId, agent: false }, size: 36, radius: 9 }))), /* @__PURE__ */ React.createElement("div", { style: { flex: 1, minWidth: 0, minHeight: 0, display: "flex", flexDirection: "column" } }, !inConversation && /* @__PURE__ */ React.createElement(DkBrowserNotice, { lang: t.lang }), !inConversation && /* @__PURE__ */ React.createElement("div", { style: { height: 46, boxSizing: "content-box", paddingTop: phone ? "env(safe-area-inset-top, 0px)" : 0, flexShrink: 0, borderBottom: "1px solid var(--line)", background: "var(--panel)", display: "flex", alignItems: "center", gap: phone ? 8 : 12, paddingLeft: phone ? 12 : 16, paddingRight: phone ? 12 : 16 } }, /* @__PURE__ */ React.createElement("svg", { width: 20, height: 20, viewBox: "0 0 24 24", fill: "none", stroke: "var(--accent)", strokeWidth: 2.2, strokeLinecap: "round", strokeLinejoin: "round", style: { display: "block", flexShrink: 0 } }, /* @__PURE__ */ React.createElement("path", { d: "m4.5 17 6-6-6-6" }), /* @__PURE__ */ React.createElement("path", { d: "M12 18.5h7.5" })), /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 14, fontWeight: 700, letterSpacing: -0.3, color: "var(--text)" } }, "beagle"), /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 12, color: "var(--faint)", whiteSpace: "nowrap" } }, "\xB7 ", (nav.find((n) => n.id === tab) || { label: T.profile }).label.toLowerCase()), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }), phone ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(StatusDot, { online: me.online }), /* @__PURE__ */ React.createElement("button", { onClick: () => setTab("profile"), title: T.profile, style: { padding: 0, border: "none", background: "transparent", cursor: "pointer", display: "flex" } }, /* @__PURE__ */ React.createElement(DkAvatar, { peer: { ...me, id: me.userId, agent: false }, size: 28, radius: 7, dot: false }))) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Tag, { tone: "accent" }, me.channel, " \xB7 lan ", me.lanVer), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 7, padding: "0 4px" } }, /* @__PURE__ */ React.createElement(StatusDot, { online: me.online }), /* @__PURE__ */ React.createElement(Mono, { size: 12.5, copy: me.ip }, me.ip)), /* @__PURE__ */ React.createElement("span", { style: { width: 1, height: 22, background: "var(--line)" } }), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 8 } }, /* @__PURE__ */ React.createElement(DkAvatar, { peer: { ...me, id: me.userId, agent: false }, size: 26, radius: 7 }), /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 12.5, fontWeight: 600, color: "var(--text)" } }, me.name)))), phone && peopleTab && /* @__PURE__ */ React.createElement("div", { style: { flexShrink: 0, display: "flex", gap: 6, padding: "8px 12px", borderBottom: "1px solid var(--line)", background: "var(--panel)", overflowX: "auto" } }, nav.filter((n) => DK_PEOPLE_TABS.includes(n.id)).map((n) => /* @__PURE__ */ React.createElement("button", { key: n.id, onClick: () => setTab(n.id), style: {
|
|
5642
|
+
display: "inline-flex",
|
|
5643
|
+
alignItems: "center",
|
|
5644
|
+
gap: 6,
|
|
5645
|
+
padding: "6px 12px",
|
|
5646
|
+
borderRadius: 999,
|
|
5647
|
+
cursor: "pointer",
|
|
5648
|
+
whiteSpace: "nowrap",
|
|
5649
|
+
border: "1px solid var(--line)",
|
|
5650
|
+
fontFamily: "var(--mono)",
|
|
5651
|
+
fontSize: 12,
|
|
5652
|
+
fontWeight: 600,
|
|
5653
|
+
background: tab === n.id ? "var(--accent)" : "transparent",
|
|
5654
|
+
color: tab === n.id ? "#fff" : "var(--dim)"
|
|
5655
|
+
} }, /* @__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) => {
|
|
5656
|
+
data.refresh();
|
|
5657
|
+
return r;
|
|
5658
|
+
}) })), 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(
|
|
5408
5659
|
DkLockedOut,
|
|
5409
5660
|
{
|
|
5410
5661
|
compact: true,
|
package/package.json
CHANGED