@decentnetwork/beagle-ui 0.2.31 → 0.2.33
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 +118 -15
- package/dist/wallet-proof.d.ts +38 -0
- package/dist/wallet-proof.js +3903 -0
- package/package.json +12 -3
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.33";
|
|
2
2
|
// src/icons.jsx
|
|
3
3
|
var ICON_PATHS = {
|
|
4
4
|
// ---- tab bar (the four must feel like one set) ----
|
|
@@ -125,6 +125,8 @@ var hostNeedsWelcome = false;
|
|
|
125
125
|
var hostPrefersRtcFile = false;
|
|
126
126
|
var hostHasKeyBackup = false;
|
|
127
127
|
var hostEnsWrite = true;
|
|
128
|
+
var hostWalletBind = true;
|
|
129
|
+
var hostAssistant = true;
|
|
128
130
|
var hostBrandHref = "";
|
|
129
131
|
var hostTabs = null;
|
|
130
132
|
var hostHideApps = null;
|
|
@@ -144,6 +146,8 @@ function setUiHost(host) {
|
|
|
144
146
|
hostPrefersRtcFile = !!H.hostPrefersRtcFile;
|
|
145
147
|
hostHasKeyBackup = !!H.hostHasKeyBackup;
|
|
146
148
|
hostEnsWrite = H.hostEnsWrite !== void 0 ? !!H.hostEnsWrite : !H.hostNeedsWelcome;
|
|
149
|
+
hostWalletBind = H.hostWalletBind !== void 0 ? !!H.hostWalletBind : hostEnsWrite;
|
|
150
|
+
hostAssistant = H.hostAssistant !== void 0 ? !!H.hostAssistant : true;
|
|
147
151
|
hostBrandHref = typeof H.hostBrandHref === "string" ? H.hostBrandHref : "";
|
|
148
152
|
}
|
|
149
153
|
function fileUrl(name) {
|
|
@@ -1354,7 +1358,9 @@ var ASSISTANT_PEER = {
|
|
|
1354
1358
|
userId: ASSISTANT_ID,
|
|
1355
1359
|
alias: "Beagle Assistant",
|
|
1356
1360
|
address: "",
|
|
1357
|
-
online:
|
|
1361
|
+
// Not a peer, so not "online": there is no session, nothing to be present
|
|
1362
|
+
// on, and no dot is drawn for an `app` row anywhere in the UI.
|
|
1363
|
+
online: false,
|
|
1358
1364
|
app: true,
|
|
1359
1365
|
agent: false,
|
|
1360
1366
|
pending: false,
|
|
@@ -1798,7 +1804,7 @@ function PeerRow({ peer, T, active, onClick, muted, isProtocol, fix }) {
|
|
|
1798
1804
|
textOverflow: "ellipsis",
|
|
1799
1805
|
flex: 1,
|
|
1800
1806
|
minWidth: 0
|
|
1801
|
-
} }, dkRowPreview(row.preview)))), /* @__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)" } }, row.lastTime), row.unread ? /* @__PURE__ */ React.createElement(Unread, { n: row.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 })));
|
|
1807
|
+
} }, dkRowPreview(row.preview)))), /* @__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)" } }, row.lastTime), peer.app ? null : row.unread ? /* @__PURE__ */ React.createElement(Unread, { n: row.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 })));
|
|
1802
1808
|
}
|
|
1803
1809
|
function dkActivityTs(p, isProtocol, fix) {
|
|
1804
1810
|
const row = rosterRow(p, isProtocol || (() => false), fix);
|
|
@@ -3499,6 +3505,67 @@ function ChatTab({ T, lang, peers, requests, activeId, thread, isProtocol, proto
|
|
|
3499
3505
|
}
|
|
3500
3506
|
Object.assign(window, { ChatTab });
|
|
3501
3507
|
|
|
3508
|
+
// src/wallet-proof.js
|
|
3509
|
+
var PROOF_KEY = { eth: "proof.eth", sol: "proof.sol" };
|
|
3510
|
+
var HEAD = "Beagle wallet binding";
|
|
3511
|
+
var LINE = "I bind this wallet to my Beagle identity.";
|
|
3512
|
+
var ADDRESS_RE = { eth: /^0x[0-9a-fA-F]{40}$/, sol: /^[1-9A-HJ-NP-Za-km-z]{32,44}$/ };
|
|
3513
|
+
var USERID_RE = /^[1-9A-HJ-NP-Za-km-z]{32,48}$/;
|
|
3514
|
+
var ISSUED_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/;
|
|
3515
|
+
function isWalletAddress(chain, address) {
|
|
3516
|
+
const re = ADDRESS_RE[chain];
|
|
3517
|
+
return !!re && typeof address === "string" && re.test(address);
|
|
3518
|
+
}
|
|
3519
|
+
function proofTime(date = /* @__PURE__ */ new Date()) {
|
|
3520
|
+
return new Date(Math.floor(date.getTime() / 1e3) * 1e3).toISOString().replace(".000Z", "Z");
|
|
3521
|
+
}
|
|
3522
|
+
function walletProofMessage({ userid, chain, address, issued }) {
|
|
3523
|
+
if (!USERID_RE.test(String(userid || "")))
|
|
3524
|
+
throw new Error("wallet proof: bad userid");
|
|
3525
|
+
if (!isWalletAddress(chain, address))
|
|
3526
|
+
throw new Error("wallet proof: bad chain or address");
|
|
3527
|
+
if (!ISSUED_RE.test(String(issued || "")))
|
|
3528
|
+
throw new Error("wallet proof: bad issued time");
|
|
3529
|
+
return [HEAD, "", LINE, "", `userid: ${userid}`, `chain: ${chain}`, `address: ${address}`, `issued: ${issued}`].join("\n");
|
|
3530
|
+
}
|
|
3531
|
+
var B58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
3532
|
+
function base58Encode(bytes) {
|
|
3533
|
+
const digits = [];
|
|
3534
|
+
for (const b of bytes) {
|
|
3535
|
+
let carry = b;
|
|
3536
|
+
for (let j = 0; j < digits.length; j++) {
|
|
3537
|
+
carry += digits[j] << 8;
|
|
3538
|
+
digits[j] = carry % 58;
|
|
3539
|
+
carry = carry / 58 | 0;
|
|
3540
|
+
}
|
|
3541
|
+
while (carry > 0) {
|
|
3542
|
+
digits.push(carry % 58);
|
|
3543
|
+
carry = carry / 58 | 0;
|
|
3544
|
+
}
|
|
3545
|
+
}
|
|
3546
|
+
let out = "";
|
|
3547
|
+
for (const b of bytes) {
|
|
3548
|
+
if (b !== 0)
|
|
3549
|
+
break;
|
|
3550
|
+
out += "1";
|
|
3551
|
+
}
|
|
3552
|
+
for (let i = digits.length - 1; i >= 0; i--)
|
|
3553
|
+
out += B58[digits[i]];
|
|
3554
|
+
return out;
|
|
3555
|
+
}
|
|
3556
|
+
var utf8Hex = (s) => "0x" + Array.from(new TextEncoder().encode(s), (b) => b.toString(16).padStart(2, "0")).join("");
|
|
3557
|
+
async function signEthProof(provider, { userid, address, issued = proofTime() }) {
|
|
3558
|
+
const message = walletProofMessage({ userid, chain: "eth", address, issued });
|
|
3559
|
+
const signature = await provider.request({ method: "personal_sign", params: [utf8Hex(message), address] });
|
|
3560
|
+
return { message, signature: String(signature) };
|
|
3561
|
+
}
|
|
3562
|
+
async function signSolProof(wallet, { userid, address, issued = proofTime() }) {
|
|
3563
|
+
const message = walletProofMessage({ userid, chain: "sol", address, issued });
|
|
3564
|
+
const r = await wallet.signMessage(new TextEncoder().encode(message), "utf8");
|
|
3565
|
+
const sig = r && r.signature ? r.signature : r;
|
|
3566
|
+
return { message, signature: base58Encode(Uint8Array.from(sig)) };
|
|
3567
|
+
}
|
|
3568
|
+
|
|
3502
3569
|
// src/profile.jsx
|
|
3503
3570
|
function CopyBtn({ value, copiedText = "Copied", copyFailedText = "Copy failed", copyTitle = "Copy" }) {
|
|
3504
3571
|
const [status, setStatus] = React.useState(null);
|
|
@@ -3913,7 +3980,7 @@ function DkEnsCard({ T, me, onRecord }) {
|
|
|
3913
3980
|
const [msg, setMsg] = React.useState(null);
|
|
3914
3981
|
const reload = () => {
|
|
3915
3982
|
fetch("/api/ens-name").then((r) => r.json()).then((d) => {
|
|
3916
|
-
setSt({ loading: false, registered: !!d.registered, record: d.record || null, mineOwned: !!d.mineOwned });
|
|
3983
|
+
setSt({ loading: false, registered: !!d.registered, record: d.record || null, mineOwned: !!d.mineOwned, writeBlocked: d.writeBlocked || "" });
|
|
3917
3984
|
if (d.registered && d.record && d.record.name)
|
|
3918
3985
|
setLabel(d.record.name.replace(/\.beagles\.eth$/i, ""));
|
|
3919
3986
|
if (onRecord)
|
|
@@ -3940,7 +4007,15 @@ function DkEnsCard({ T, me, onRecord }) {
|
|
|
3940
4007
|
if (l)
|
|
3941
4008
|
post("/api/ens-register", { name: l, displayName: me.name, description: me.description || "" });
|
|
3942
4009
|
};
|
|
3943
|
-
const bind = (chain, address) => post("/api/ens-bind-wallet", { chain, address });
|
|
4010
|
+
const bind = (chain, address, proof) => post("/api/ens-bind-wallet", proof ? { chain, address, proof } : { chain, address });
|
|
4011
|
+
const signing = () => {
|
|
4012
|
+
setBusy(true);
|
|
4013
|
+
setMsg({ tone: "ok", text: T.ensSigning || "approve the signature in your wallet\u2026" });
|
|
4014
|
+
};
|
|
4015
|
+
const walletErr = (e) => {
|
|
4016
|
+
setBusy(false);
|
|
4017
|
+
setMsg({ tone: "err", text: String(e && e.message || e) });
|
|
4018
|
+
};
|
|
3944
4019
|
const bindEth = async () => {
|
|
3945
4020
|
const eth = window.ethereum;
|
|
3946
4021
|
if (!eth) {
|
|
@@ -3949,10 +4024,13 @@ function DkEnsCard({ T, me, onRecord }) {
|
|
|
3949
4024
|
}
|
|
3950
4025
|
try {
|
|
3951
4026
|
const a = await eth.request({ method: "eth_requestAccounts" });
|
|
3952
|
-
if (a
|
|
3953
|
-
|
|
4027
|
+
if (!a || !a[0])
|
|
4028
|
+
return;
|
|
4029
|
+
signing();
|
|
4030
|
+
const proof = await signEthProof(eth, { userid: me.userId, address: a[0] });
|
|
4031
|
+
bind("eth", a[0], proof);
|
|
3954
4032
|
} catch (e) {
|
|
3955
|
-
|
|
4033
|
+
walletErr(e);
|
|
3956
4034
|
}
|
|
3957
4035
|
};
|
|
3958
4036
|
const bindSol = async () => {
|
|
@@ -3964,10 +4042,13 @@ function DkEnsCard({ T, me, onRecord }) {
|
|
|
3964
4042
|
try {
|
|
3965
4043
|
const r = await sol.connect();
|
|
3966
4044
|
const a = r && r.publicKey && r.publicKey.toString();
|
|
3967
|
-
if (a)
|
|
3968
|
-
|
|
4045
|
+
if (!a)
|
|
4046
|
+
return;
|
|
4047
|
+
signing();
|
|
4048
|
+
const proof = await signSolProof(sol, { userid: me.userId, address: a });
|
|
4049
|
+
bind("sol", a, proof);
|
|
3969
4050
|
} catch (e) {
|
|
3970
|
-
|
|
4051
|
+
walletErr(e);
|
|
3971
4052
|
}
|
|
3972
4053
|
};
|
|
3973
4054
|
const field = { flex: 1, minWidth: 0, height: 34, borderRadius: 8, border: "1px solid var(--line)", background: "var(--panel-2)", color: "var(--text)", fontFamily: "var(--mono)", fontSize: 13, padding: "0 10px", outline: "none" };
|
|
@@ -4016,6 +4097,7 @@ function DkEnsCard({ T, me, onRecord }) {
|
|
|
4016
4097
|
const [picking, setPicking] = React.useState(false);
|
|
4017
4098
|
const fileRef = React.useRef(null);
|
|
4018
4099
|
const canWrite = hostEnsWrite && !!st.mineOwned;
|
|
4100
|
+
const canBind = hostWalletBind && !!st.mineOwned && !st.writeBlocked;
|
|
4019
4101
|
const rec = st.record;
|
|
4020
4102
|
const boundEth = rec && rec.addresses && rec.addresses["60"];
|
|
4021
4103
|
const boundSol = rec && rec.addresses && rec.addresses["501"];
|
|
@@ -4038,7 +4120,16 @@ function DkEnsCard({ T, me, onRecord }) {
|
|
|
4038
4120
|
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) });
|
|
4039
4121
|
});
|
|
4040
4122
|
};
|
|
4041
|
-
const
|
|
4123
|
+
const proven = (chain, bound) => {
|
|
4124
|
+
if (rec && rec.owner && String(rec.owner).toLowerCase() === String(bound).toLowerCase())
|
|
4125
|
+
return true;
|
|
4126
|
+
try {
|
|
4127
|
+
return JSON.parse((rec && rec.texts || {})[PROOF_KEY[chain]]).msg.indexOf("address: " + bound + "\n") >= 0;
|
|
4128
|
+
} catch {
|
|
4129
|
+
return false;
|
|
4130
|
+
}
|
|
4131
|
+
};
|
|
4132
|
+
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, display: "flex", flexDirection: "column", gap: 2 } }, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 12.5, color: "var(--text)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, bound), /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--ui)", fontSize: 11, color: proven(chain, bound) ? "var(--online)" : "var(--faint)" } }, proven(chain, bound) ? T.ensProven || "signed by this wallet" : T.ensUnproven || "no wallet signature yet")), /* @__PURE__ */ React.createElement(CopyBtn, { value: bound, copiedText: T.copied, copyFailedText: T.copyFailed, copyTitle: T.copy }), !proven(chain, bound) && /* @__PURE__ */ React.createElement(Btn, { size: "sm", disabled: busy || !canBind, onClick: onBind }, T.ensProve || "sign"), /* @__PURE__ */ React.createElement(Btn, { size: "sm", tone: "danger", disabled: busy || !canBind, 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 || !canBind, onClick: onBind }, T.ensBind || "bind")));
|
|
4042
4133
|
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, !hostEnsWrite ? /* @__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"), st.registered && rec && rec.name ? /* @__PURE__ */ React.createElement("span", { style: { flex: 1, minWidth: 0, fontFamily: "var(--mono)", fontSize: 13, color: "var(--text)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, rec.name) : /* @__PURE__ */ React.createElement("span", { style: { flex: 1, minWidth: 0, fontFamily: "var(--ui)", fontSize: 12, lineHeight: 1.5, color: "var(--faint)" } }, T.ensWebOnly || "Registering a beagles.eth name needs the Beagle phone or desktop app. Your avatar can be set above without one.")) : /* @__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(
|
|
4043
4134
|
"input",
|
|
4044
4135
|
{
|
|
@@ -4052,7 +4143,7 @@ function DkEnsCard({ T, me, onRecord }) {
|
|
|
4052
4143
|
},
|
|
4053
4144
|
style: { ...field, minWidth: 110 }
|
|
4054
4145
|
}
|
|
4055
|
-
), /* @__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 || !canWrite, 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 || !canWrite, 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 || !canWrite, onClick: () => fileRef.current && fileRef.current.click() }, T.ensUpload || "upload"), /* @__PURE__ */ React.createElement(Btn, { size: "sm", disabled: busy || !canWrite, 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))));
|
|
4146
|
+
), /* @__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 || !canWrite, 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 || !canWrite, 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 || !canWrite, onClick: () => fileRef.current && fileRef.current.click() }, T.ensUpload || "upload"), /* @__PURE__ */ React.createElement(Btn, { size: "sm", disabled: busy || !canWrite, 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 && !st.writeBlocked), st.writeBlocked && hostWalletBind && /* @__PURE__ */ React.createElement("div", { style: { padding: "10px 16px", borderBottom: msg ? "1px solid var(--line)" : "none", fontFamily: "var(--ui)", fontSize: 12, lineHeight: 1.5, color: "var(--faint)" } }, st.writeBlocked), 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))));
|
|
4056
4147
|
}
|
|
4057
4148
|
function DkUpdateCheck({ T }) {
|
|
4058
4149
|
const [st, setSt] = React.useState("idle");
|
|
@@ -4407,6 +4498,8 @@ function DkImgAvatar({ url, seed, size, radius }) {
|
|
|
4407
4498
|
}
|
|
4408
4499
|
function DkAvatar({ peer, size = 30, radius = 7, dot = true }) {
|
|
4409
4500
|
const d = Math.max(8, size * 0.3);
|
|
4501
|
+
if (peer && peer.app)
|
|
4502
|
+
dot = false;
|
|
4410
4503
|
return /* @__PURE__ */ React.createElement("div", { style: { position: "relative", width: size, height: size, flexShrink: 0 } }, peer.agent ? /* @__PURE__ */ React.createElement(DkBot, { seed: peer.id, size, radius }) : peer.avatarUrl ? /* @__PURE__ */ React.createElement(DkImgAvatar, { url: peer.avatarUrl, seed: peer.userId || peer.id, size, radius }) : peer.punkId != null ? /* @__PURE__ */ React.createElement(DkPunkAvatar, { id: peer.punkId, size, radius, fallbackSeed: peer.userId || peer.id }) : /* @__PURE__ */ React.createElement(DkIdenticon, { seed: peer.userId || peer.id, size, radius }), dot && /* @__PURE__ */ React.createElement("span", { style: {
|
|
4411
4504
|
position: "absolute",
|
|
4412
4505
|
right: -2,
|
|
@@ -6168,6 +6261,8 @@ function useIncomingAlerts({ T, peers, requests, loaded, activeId, sound = true,
|
|
|
6168
6261
|
return;
|
|
6169
6262
|
const byId = /* @__PURE__ */ new Map();
|
|
6170
6263
|
for (const p of peers || []) {
|
|
6264
|
+
if (p && p.app)
|
|
6265
|
+
continue;
|
|
6171
6266
|
const dir = p.lastDir || (String(p.lastMsg || "").startsWith("you: ") ? "out" : "in");
|
|
6172
6267
|
const quiet = typeof isProtocol === "function" && lastIsProtocol(p, isProtocol);
|
|
6173
6268
|
byId.set(p.id, { ts: p.lastTs || 0, dir, quiet });
|
|
@@ -6448,6 +6543,10 @@ var STR = {
|
|
|
6448
6543
|
ensUnbind: "unbind",
|
|
6449
6544
|
ensNotBound: "not bound",
|
|
6450
6545
|
ensWebOnly: "Registering a beagles.eth name needs the Beagle phone or desktop app. Your avatar can be set above without one.",
|
|
6546
|
+
ensProven: "signed by this wallet",
|
|
6547
|
+
ensUnproven: "no wallet signature yet",
|
|
6548
|
+
ensProve: "sign",
|
|
6549
|
+
ensSigning: "approve the signature in your wallet\u2026",
|
|
6451
6550
|
ensAvatar: "avatar",
|
|
6452
6551
|
ensChoose: "choose",
|
|
6453
6552
|
ensClear: "clear",
|
|
@@ -6643,6 +6742,10 @@ var STR = {
|
|
|
6643
6742
|
ensUnbind: "\u89E3\u7ED1",
|
|
6644
6743
|
ensNotBound: "\u672A\u7ED1\u5B9A",
|
|
6645
6744
|
ensWebOnly: "\u6CE8\u518C beagles.eth \u540D\u5B57\u9700\u8981 Beagle \u624B\u673A\u6216\u684C\u9762\u5E94\u7528\u3002\u5934\u50CF\u4E0D\u9700\u8981\u540D\u5B57\uFF0C\u53EF\u5728\u4E0A\u65B9\u76F4\u63A5\u8BBE\u7F6E\u3002",
|
|
6745
|
+
ensProven: "\u5DF2\u7531\u8BE5\u94B1\u5305\u7B7E\u540D",
|
|
6746
|
+
ensUnproven: "\u5C1A\u65E0\u94B1\u5305\u7B7E\u540D",
|
|
6747
|
+
ensProve: "\u7B7E\u540D",
|
|
6748
|
+
ensSigning: "\u8BF7\u5728\u94B1\u5305\u4E2D\u786E\u8BA4\u7B7E\u540D\u2026",
|
|
6646
6749
|
ensAvatar: "\u5934\u50CF",
|
|
6647
6750
|
ensChoose: "\u9009\u62E9",
|
|
6648
6751
|
ensClear: "\u6E05\u9664",
|
|
@@ -6977,10 +7080,10 @@ function DkApp() {
|
|
|
6977
7080
|
window.location.reload();
|
|
6978
7081
|
}, [me && me.beagleVer]);
|
|
6979
7082
|
const [assistantTs, setAssistantTs] = React.useState(loadAssistantTs);
|
|
6980
|
-
const peers = React.useMemo(() => (data.peers || []).concat([Object.assign({}, ASSISTANT_PEER, {
|
|
7083
|
+
const peers = React.useMemo(() => (data.peers || []).concat(hostAssistant ? [Object.assign({}, ASSISTANT_PEER, {
|
|
6981
7084
|
lastTs: assistantTs,
|
|
6982
7085
|
lastTime: assistantTs ? dkClock(assistantTs) : ""
|
|
6983
|
-
})]), [data.peers, assistantTs]);
|
|
7086
|
+
})] : []), [data.peers, assistantTs]);
|
|
6984
7087
|
const [pendingAddr, setPendingAddr] = React.useState(() => dkParseDeepLinkAddress());
|
|
6985
7088
|
React.useEffect(() => {
|
|
6986
7089
|
const onHash = () => {
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Types for @decentnetwork/beagle-ui/wallet-proof. Copied to dist/ by the build.
|
|
2
|
+
export type WalletChain = 'eth' | 'sol';
|
|
3
|
+
export interface WalletProof { message: string; signature: string }
|
|
4
|
+
export type VerifyResult = { ok: true; issued: string } | { ok: false; error: string };
|
|
5
|
+
|
|
6
|
+
export const WALLET_PROOF_VERSION: 1;
|
|
7
|
+
export const WALLET_CHAINS: WalletChain[];
|
|
8
|
+
export const COIN_TYPE: { eth: '60'; sol: '501' };
|
|
9
|
+
export const PROOF_KEY: { eth: 'proof.eth'; sol: 'proof.sol' };
|
|
10
|
+
|
|
11
|
+
export function isWalletAddress(chain: string, address: unknown): boolean;
|
|
12
|
+
export function proofTime(date?: Date): string;
|
|
13
|
+
export function walletProofMessage(f: { userid: string; chain: WalletChain; address: string; issued: string }): string;
|
|
14
|
+
export function parseWalletProofMessage(msg: unknown): { userid: string; chain: WalletChain; address: string; issued: string } | null;
|
|
15
|
+
export function encodeWalletProof(p: WalletProof): string;
|
|
16
|
+
export function decodeWalletProof(text: unknown): WalletProof | null;
|
|
17
|
+
export function base58Encode(bytes: Uint8Array): string;
|
|
18
|
+
export function base58Decode(str: string): Uint8Array;
|
|
19
|
+
export function signEthProof(provider: { request(a: { method: string; params: unknown[] }): Promise<unknown> }, f: { userid: string; address: string; issued?: string }): Promise<WalletProof>;
|
|
20
|
+
export function signSolProof(wallet: { signMessage(m: Uint8Array, display?: string): Promise<unknown> }, f: { userid: string; address: string; issued?: string }): Promise<WalletProof>;
|
|
21
|
+
|
|
22
|
+
export function eip191Hash(message: string): Uint8Array;
|
|
23
|
+
export function recoverEthAddress(message: string, signature: string): string;
|
|
24
|
+
export function verifyWalletProof(a: {
|
|
25
|
+
chain: string; address: string; userid: string;
|
|
26
|
+
proof: string | WalletProof | null | undefined;
|
|
27
|
+
maxAgeMs?: number; now?: number;
|
|
28
|
+
}): VerifyResult;
|
|
29
|
+
export function verifyRecordWallet(
|
|
30
|
+
record: { owner?: string; addresses?: Record<string, string>; texts?: Record<string, string> } | null | undefined,
|
|
31
|
+
chain: WalletChain
|
|
32
|
+
): { ok: true; address: string; via: 'proof'; issued: string } | { ok: true; address: string; via: 'owner' } | { ok: false; error: string };
|
|
33
|
+
|
|
34
|
+
export const BIND_MAX_AGE_MS: number;
|
|
35
|
+
export function applyWalletBind(
|
|
36
|
+
record: { addresses?: Record<string, string>; texts?: Record<string, string> } | null | undefined,
|
|
37
|
+
req: { chain: string; address: unknown; proof?: string | WalletProof | null; userid: string; now?: number }
|
|
38
|
+
): { ok: true; addresses: Record<string, string>; texts: Record<string, string> } | { ok: false; status: number; error: string };
|