@decentnetwork/beagle-ui 0.2.7 → 0.2.9
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 +243 -9
- 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.9";
|
|
2
2
|
// src/icons.jsx
|
|
3
3
|
var ICON_PATHS = {
|
|
4
4
|
// ---- tab bar (the four must feel like one set) ----
|
|
@@ -384,11 +384,13 @@ function useDaemonData() {
|
|
|
384
384
|
const t = setInterval(refresh, 2500);
|
|
385
385
|
return () => clearInterval(t);
|
|
386
386
|
}, [refresh]);
|
|
387
|
+
const inFlight = React.useRef(/* @__PURE__ */ new Set());
|
|
387
388
|
const loadThread = React.useCallback(async (peerId) => {
|
|
388
|
-
if (!peerId)
|
|
389
|
+
if (!peerId || inFlight.current.has(peerId))
|
|
389
390
|
return;
|
|
391
|
+
inFlight.current.add(peerId);
|
|
390
392
|
try {
|
|
391
|
-
const d = await dkGet("/api/chat-history?peer=" + encodeURIComponent(peerId));
|
|
393
|
+
const d = await dkGet("/api/chat-history?peer=" + encodeURIComponent(peerId) + "&limit=200");
|
|
392
394
|
const arr = d.chats && d.chats[peerId] || [];
|
|
393
395
|
await prefetchThreadFiles(arr);
|
|
394
396
|
const msgs = arr.map((m) => ({
|
|
@@ -421,6 +423,8 @@ function useDaemonData() {
|
|
|
421
423
|
const withDay = msgs.length ? [{ day: dkDayLabel(arr[0].ts) }].concat(msgs) : [];
|
|
422
424
|
setThreads((t) => Object.assign({}, t, { [peerId]: withDay }));
|
|
423
425
|
} catch (e) {
|
|
426
|
+
} finally {
|
|
427
|
+
inFlight.current.delete(peerId);
|
|
424
428
|
}
|
|
425
429
|
}, []);
|
|
426
430
|
return Object.assign({}, snap, { threads, refresh, loadThread });
|
|
@@ -527,6 +531,9 @@ function PeerRow({ peer, T, active, onClick }) {
|
|
|
527
531
|
minWidth: 0
|
|
528
532
|
} }, dkContactPreview(peer.lastMsg) || peer.lastMsg))), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", flexDirection: "column", alignItems: "flex-end", gap: 4, flexShrink: 0 } }, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 10.5, color: "var(--faint)" } }, peer.lastTime), peer.unread ? /* @__PURE__ */ React.createElement(Unread, { n: peer.unread }) : peer.pending ? /* @__PURE__ */ React.createElement(Icon, { name: "clock", size: 11, stroke: 2.2, color: "var(--warn, #d29922)", title: "friend request pending" }) : /* @__PURE__ */ React.createElement(StatusDot, { online: peer.online })));
|
|
529
533
|
}
|
|
534
|
+
function dkActivityTs(p) {
|
|
535
|
+
return Math.max(p.lastTs || 0, p.acceptedAt || 0, p.requestedAt || 0);
|
|
536
|
+
}
|
|
530
537
|
function PeerSidebar({ T, peers, requests, activeId, onSelect, onAct, onAdd, prefillAddr, onPrefillConsumed }) {
|
|
531
538
|
const [q, setQ] = React.useState("");
|
|
532
539
|
const [addr, setAddr] = React.useState("");
|
|
@@ -564,7 +571,7 @@ function PeerSidebar({ T, peers, requests, activeId, onSelect, onAct, onAdd, pre
|
|
|
564
571
|
const s = (p.alias || "") + " " + p.userId + " " + p.ip;
|
|
565
572
|
return s.toLowerCase().includes(q.toLowerCase());
|
|
566
573
|
}).slice().sort((a, b) => {
|
|
567
|
-
const ta = a
|
|
574
|
+
const ta = dkActivityTs(a), tb = dkActivityTs(b);
|
|
568
575
|
if (ta !== tb)
|
|
569
576
|
return tb - ta;
|
|
570
577
|
return String(a.alias || a.userId || "").localeCompare(String(b.alias || b.userId || ""));
|
|
@@ -2725,6 +2732,35 @@ function DkDisplaySettings({ t, setTweak }) {
|
|
|
2725
2732
|
)));
|
|
2726
2733
|
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"]])));
|
|
2727
2734
|
}
|
|
2735
|
+
function DkAlertSettings({ t, setTweak }) {
|
|
2736
|
+
const zh = t.lang === "zh";
|
|
2737
|
+
const toggle = (key, label, sub, last) => /* @__PURE__ */ React.createElement("label", { style: {
|
|
2738
|
+
display: "flex",
|
|
2739
|
+
alignItems: "center",
|
|
2740
|
+
gap: 13,
|
|
2741
|
+
padding: "13px 16px",
|
|
2742
|
+
cursor: "pointer",
|
|
2743
|
+
borderBottom: last ? "none" : "1px solid var(--line)"
|
|
2744
|
+
} }, /* @__PURE__ */ React.createElement(
|
|
2745
|
+
"input",
|
|
2746
|
+
{
|
|
2747
|
+
type: "checkbox",
|
|
2748
|
+
checked: t[key] !== false,
|
|
2749
|
+
onChange: (e) => setTweak(key, e.target.checked),
|
|
2750
|
+
style: { width: 16, height: 16, accentColor: "var(--accent)" }
|
|
2751
|
+
}
|
|
2752
|
+
), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }, /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 13.5, fontWeight: 600, color: "var(--text)" } }, label), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 11.5, color: "var(--dim)", marginTop: 2 } }, sub)));
|
|
2753
|
+
return /* @__PURE__ */ React.createElement(Card, { label: zh ? "\u63D0\u9192" : "Alerts" }, toggle(
|
|
2754
|
+
"alertSound",
|
|
2755
|
+
zh ? "\u65B0\u6D88\u606F\u63D0\u793A\u97F3" : "Sound for new messages",
|
|
2756
|
+
zh ? "\u6536\u5230\u65B0\u6D88\u606F\u6216\u597D\u53CB\u8BF7\u6C42\u65F6\u64AD\u653E\u77ED\u4FC3\u63D0\u793A\u97F3\u3002\u6B63\u5728\u770B\u7684\u5F53\u524D\u4F1A\u8BDD\u4E0D\u4F1A\u54CD\u3002" : "A short chime when a message or friend request arrives. Silent for the conversation you are reading."
|
|
2757
|
+
), toggle(
|
|
2758
|
+
"alertFlash",
|
|
2759
|
+
zh ? "\u6807\u7B7E\u9875\u95EA\u70C1" : "Flash the tab",
|
|
2760
|
+
zh ? "\u7A97\u53E3\u4E0D\u5728\u524D\u53F0\u65F6\uFF0C\u6807\u7B7E\u6807\u9898\u548C\u56FE\u6807\u95EA\u70C1\u63D0\u793A\uFF0C\u56DE\u5230\u7A97\u53E3\u5373\u505C\u6B62\u3002" : "While this window is in the background, the tab title and icon blink until you come back.",
|
|
2761
|
+
true
|
|
2762
|
+
));
|
|
2763
|
+
}
|
|
2728
2764
|
function ProfileTab({ T, me, onEdit, t, setTweak }) {
|
|
2729
2765
|
var _a;
|
|
2730
2766
|
const [qr, setQr] = React.useState(null);
|
|
@@ -2746,7 +2782,7 @@ function ProfileTab({ T, me, onEdit, t, setTweak }) {
|
|
|
2746
2782
|
},
|
|
2747
2783
|
style: { width: 16, height: 16, accentColor: "var(--accent)" }
|
|
2748
2784
|
}
|
|
2749
|
-
), /* @__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(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) => {
|
|
2785
|
+
), /* @__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) => {
|
|
2750
2786
|
if (onEdit)
|
|
2751
2787
|
onEdit(name, description);
|
|
2752
2788
|
setEditing(false);
|
|
@@ -4310,6 +4346,183 @@ function NetworkTab({ T, me, peers, exits, activeExit, reqCount, onSetExit, onOp
|
|
|
4310
4346
|
}
|
|
4311
4347
|
Object.assign(window, { NetworkTab });
|
|
4312
4348
|
|
|
4349
|
+
// src/notify.jsx
|
|
4350
|
+
var dkAudioCtx = null;
|
|
4351
|
+
function dkAudio() {
|
|
4352
|
+
if (dkAudioCtx && dkAudioCtx.state !== "closed")
|
|
4353
|
+
return dkAudioCtx;
|
|
4354
|
+
try {
|
|
4355
|
+
dkAudioCtx = new (window.AudioContext || window.webkitAudioContext)();
|
|
4356
|
+
} catch (e) {
|
|
4357
|
+
dkAudioCtx = null;
|
|
4358
|
+
}
|
|
4359
|
+
return dkAudioCtx;
|
|
4360
|
+
}
|
|
4361
|
+
function dkUnlockAudioOnGesture() {
|
|
4362
|
+
const unlock = () => {
|
|
4363
|
+
const c = dkAudio();
|
|
4364
|
+
if (c && c.state === "suspended")
|
|
4365
|
+
c.resume().catch(() => {
|
|
4366
|
+
});
|
|
4367
|
+
};
|
|
4368
|
+
window.addEventListener("pointerdown", unlock, { passive: true });
|
|
4369
|
+
window.addEventListener("keydown", unlock, { passive: true });
|
|
4370
|
+
return () => {
|
|
4371
|
+
window.removeEventListener("pointerdown", unlock);
|
|
4372
|
+
window.removeEventListener("keydown", unlock);
|
|
4373
|
+
};
|
|
4374
|
+
}
|
|
4375
|
+
function dkChime(kind) {
|
|
4376
|
+
const c = dkAudio();
|
|
4377
|
+
if (!c)
|
|
4378
|
+
return;
|
|
4379
|
+
if (c.state === "suspended")
|
|
4380
|
+
c.resume().catch(() => {
|
|
4381
|
+
});
|
|
4382
|
+
const notes = kind === "request" ? [[660, 0], [880, 0.14]] : [[880, 0], [1175, 0.11]];
|
|
4383
|
+
const t0 = c.currentTime;
|
|
4384
|
+
for (const [freq, at] of notes) {
|
|
4385
|
+
const o = c.createOscillator(), g = c.createGain();
|
|
4386
|
+
o.type = "sine";
|
|
4387
|
+
o.frequency.value = freq;
|
|
4388
|
+
o.connect(g);
|
|
4389
|
+
g.connect(c.destination);
|
|
4390
|
+
const t = t0 + at;
|
|
4391
|
+
g.gain.setValueAtTime(1e-4, t);
|
|
4392
|
+
g.gain.exponentialRampToValueAtTime(0.18, t + 0.015);
|
|
4393
|
+
g.gain.exponentialRampToValueAtTime(1e-4, t + 0.17);
|
|
4394
|
+
o.start(t);
|
|
4395
|
+
o.stop(t + 0.2);
|
|
4396
|
+
}
|
|
4397
|
+
}
|
|
4398
|
+
var dkFlash = { timer: null, title: "", link: null, href: null, badged: null, on: false, n: 0, label: "" };
|
|
4399
|
+
function dkBadgedIcon(href) {
|
|
4400
|
+
return new Promise((res, rej) => {
|
|
4401
|
+
const img = new Image();
|
|
4402
|
+
img.onload = () => {
|
|
4403
|
+
try {
|
|
4404
|
+
if (!img.naturalWidth)
|
|
4405
|
+
return rej(new Error("icon has no intrinsic size"));
|
|
4406
|
+
const s = 64, cv = document.createElement("canvas");
|
|
4407
|
+
cv.width = s;
|
|
4408
|
+
cv.height = s;
|
|
4409
|
+
const cx = cv.getContext("2d");
|
|
4410
|
+
cx.drawImage(img, 0, 0, s, s);
|
|
4411
|
+
cx.beginPath();
|
|
4412
|
+
cx.arc(s - 14, 14, 12, 0, Math.PI * 2);
|
|
4413
|
+
cx.fillStyle = "#e5484d";
|
|
4414
|
+
cx.fill();
|
|
4415
|
+
cx.lineWidth = 3;
|
|
4416
|
+
cx.strokeStyle = "#fff";
|
|
4417
|
+
cx.stroke();
|
|
4418
|
+
res(cv.toDataURL("image/png"));
|
|
4419
|
+
} catch (e) {
|
|
4420
|
+
rej(e);
|
|
4421
|
+
}
|
|
4422
|
+
};
|
|
4423
|
+
img.onerror = () => rej(new Error("icon failed to load"));
|
|
4424
|
+
img.src = href;
|
|
4425
|
+
});
|
|
4426
|
+
}
|
|
4427
|
+
function dkFlashTick() {
|
|
4428
|
+
dkFlash.on = !dkFlash.on;
|
|
4429
|
+
document.title = `(${dkFlash.n}) ${dkFlash.on ? dkFlash.label : dkFlash.title}`;
|
|
4430
|
+
}
|
|
4431
|
+
function dkFlashStart(label, count) {
|
|
4432
|
+
dkFlash.n += count || 1;
|
|
4433
|
+
dkFlash.label = label;
|
|
4434
|
+
if (dkFlash.timer)
|
|
4435
|
+
return;
|
|
4436
|
+
dkFlash.title = document.title;
|
|
4437
|
+
dkFlash.link = document.querySelector('link[rel~="icon"]');
|
|
4438
|
+
dkFlash.href = dkFlash.link ? dkFlash.link.href : null;
|
|
4439
|
+
dkFlash.badged = null;
|
|
4440
|
+
dkFlash.on = false;
|
|
4441
|
+
if (dkFlash.href) {
|
|
4442
|
+
dkBadgedIcon(dkFlash.href).then((b) => {
|
|
4443
|
+
dkFlash.badged = b;
|
|
4444
|
+
if (dkFlash.timer && dkFlash.link)
|
|
4445
|
+
dkFlash.link.href = b;
|
|
4446
|
+
}).catch(() => {
|
|
4447
|
+
});
|
|
4448
|
+
}
|
|
4449
|
+
dkFlashTick();
|
|
4450
|
+
dkFlash.timer = setInterval(dkFlashTick, 1e3);
|
|
4451
|
+
}
|
|
4452
|
+
function dkFlashStop() {
|
|
4453
|
+
if (!dkFlash.timer)
|
|
4454
|
+
return;
|
|
4455
|
+
clearInterval(dkFlash.timer);
|
|
4456
|
+
dkFlash.timer = null;
|
|
4457
|
+
dkFlash.n = 0;
|
|
4458
|
+
document.title = dkFlash.title;
|
|
4459
|
+
if (dkFlash.link && dkFlash.href)
|
|
4460
|
+
dkFlash.link.href = dkFlash.href;
|
|
4461
|
+
}
|
|
4462
|
+
function dkLooking() {
|
|
4463
|
+
return document.visibilityState === "visible" && document.hasFocus();
|
|
4464
|
+
}
|
|
4465
|
+
function useIncomingAlerts({ T, peers, requests, loaded, activeId, sound = true, flash = true }) {
|
|
4466
|
+
const prev = React.useRef(null);
|
|
4467
|
+
React.useEffect(() => dkUnlockAudioOnGesture(), []);
|
|
4468
|
+
React.useEffect(() => {
|
|
4469
|
+
if (!flash)
|
|
4470
|
+
dkFlashStop();
|
|
4471
|
+
}, [flash]);
|
|
4472
|
+
React.useEffect(() => {
|
|
4473
|
+
const seen = () => {
|
|
4474
|
+
if (dkLooking())
|
|
4475
|
+
dkFlashStop();
|
|
4476
|
+
};
|
|
4477
|
+
window.addEventListener("focus", seen);
|
|
4478
|
+
window.addEventListener("pointerdown", seen, { passive: true });
|
|
4479
|
+
document.addEventListener("visibilitychange", seen);
|
|
4480
|
+
return () => {
|
|
4481
|
+
window.removeEventListener("focus", seen);
|
|
4482
|
+
window.removeEventListener("pointerdown", seen);
|
|
4483
|
+
document.removeEventListener("visibilitychange", seen);
|
|
4484
|
+
dkFlashStop();
|
|
4485
|
+
};
|
|
4486
|
+
}, []);
|
|
4487
|
+
React.useEffect(() => {
|
|
4488
|
+
if (!loaded)
|
|
4489
|
+
return;
|
|
4490
|
+
const byId = /* @__PURE__ */ new Map();
|
|
4491
|
+
for (const p of peers || []) {
|
|
4492
|
+
const dir = p.lastDir || (String(p.lastMsg || "").startsWith("you: ") ? "out" : "in");
|
|
4493
|
+
byId.set(p.id, { ts: p.lastTs || 0, dir });
|
|
4494
|
+
}
|
|
4495
|
+
const reqIds = new Set((requests || []).map((r) => r.id));
|
|
4496
|
+
const was = prev.current;
|
|
4497
|
+
prev.current = { byId, reqIds };
|
|
4498
|
+
if (!was)
|
|
4499
|
+
return;
|
|
4500
|
+
let msgs = 0, elsewhere = 0, reqs = 0;
|
|
4501
|
+
for (const [id, cur] of byId) {
|
|
4502
|
+
if (cur.dir !== "in" || !cur.ts)
|
|
4503
|
+
continue;
|
|
4504
|
+
const old = was.byId.get(id);
|
|
4505
|
+
if (old ? cur.ts <= old.ts : Date.now() - cur.ts > 3e4)
|
|
4506
|
+
continue;
|
|
4507
|
+
msgs += 1;
|
|
4508
|
+
if (id !== activeId)
|
|
4509
|
+
elsewhere += 1;
|
|
4510
|
+
}
|
|
4511
|
+
for (const id of reqIds)
|
|
4512
|
+
if (!was.reqIds.has(id))
|
|
4513
|
+
reqs += 1;
|
|
4514
|
+
if (!msgs && !reqs)
|
|
4515
|
+
return;
|
|
4516
|
+
const looking = dkLooking();
|
|
4517
|
+
if (sound && !(looking && !reqs && !elsewhere))
|
|
4518
|
+
dkChime(reqs && !msgs ? "request" : "message");
|
|
4519
|
+
if (flash && !looking) {
|
|
4520
|
+
const label = msgs ? T && T.alertMsg || "New message" : T && T.alertReq || "New friend request";
|
|
4521
|
+
dkFlashStart(label, msgs + reqs);
|
|
4522
|
+
}
|
|
4523
|
+
}, [peers, requests, loaded, activeId, T, sound, flash]);
|
|
4524
|
+
}
|
|
4525
|
+
|
|
4313
4526
|
// src/settings-store.jsx
|
|
4314
4527
|
var TWEAKS_STORAGE_KEY = "decentlan.tweaks";
|
|
4315
4528
|
function useTweaks(defaults) {
|
|
@@ -4370,7 +4583,9 @@ var DK_DEFAULTS = (
|
|
|
4370
4583
|
"accent": "#7B6CF6",
|
|
4371
4584
|
"density": "compact",
|
|
4372
4585
|
"lang": "en",
|
|
4373
|
-
"startTab": "chat"
|
|
4586
|
+
"startTab": "chat",
|
|
4587
|
+
"alertSound": true,
|
|
4588
|
+
"alertFlash": true
|
|
4374
4589
|
}
|
|
4375
4590
|
);
|
|
4376
4591
|
function dkDetectLang() {
|
|
@@ -4439,6 +4654,8 @@ var STR = {
|
|
|
4439
4654
|
network: "Network",
|
|
4440
4655
|
apps: "Apps",
|
|
4441
4656
|
profile: "Profile",
|
|
4657
|
+
alertMsg: "New message",
|
|
4658
|
+
alertReq: "New friend request",
|
|
4442
4659
|
recommended: "Discover",
|
|
4443
4660
|
registered: "Names",
|
|
4444
4661
|
here: "Here",
|
|
@@ -4619,6 +4836,8 @@ var STR = {
|
|
|
4619
4836
|
network: "\u7F51\u7EDC",
|
|
4620
4837
|
apps: "\u5E94\u7528",
|
|
4621
4838
|
profile: "\u6211\u7684",
|
|
4839
|
+
alertMsg: "\u65B0\u6D88\u606F",
|
|
4840
|
+
alertReq: "\u65B0\u7684\u597D\u53CB\u8BF7\u6C42",
|
|
4622
4841
|
recommended: "\u63A8\u8350",
|
|
4623
4842
|
registered: "\u540D\u5F55",
|
|
4624
4843
|
here: "\u5728\u573A",
|
|
@@ -5011,6 +5230,15 @@ function DkApp() {
|
|
|
5011
5230
|
const T = STR[t.lang] || STR.en;
|
|
5012
5231
|
const vars = dkTheme(t.theme, t.accent);
|
|
5013
5232
|
const rowPad = t.density === "comfortable" ? "11px 12px" : "7px 10px";
|
|
5233
|
+
useIncomingAlerts({
|
|
5234
|
+
T,
|
|
5235
|
+
peers,
|
|
5236
|
+
requests,
|
|
5237
|
+
loaded: data.loaded,
|
|
5238
|
+
activeId,
|
|
5239
|
+
sound: t.alertSound !== false,
|
|
5240
|
+
flash: t.alertFlash !== false
|
|
5241
|
+
});
|
|
5014
5242
|
const callCtl = useCallController(me.userId, (peerId, video, direction) => {
|
|
5015
5243
|
const text = `WebRTC ${video ? "video" : "audio"} call: ${direction}`;
|
|
5016
5244
|
dkApi.logLocal(peerId, direction === "outgoing" ? "out" : "in", text).then(() => {
|
|
@@ -5152,10 +5380,16 @@ function DkApp() {
|
|
|
5152
5380
|
{ id: "recommended", icon: "sparkles", label: T.recommended },
|
|
5153
5381
|
{ id: "registered", icon: "at", label: T.registered },
|
|
5154
5382
|
{ id: "network", icon: "network", label: T.network },
|
|
5155
|
-
{ id: "apps", icon: "grid", label: T.apps }
|
|
5156
|
-
{ id: "profile", icon: "userRound", label: T.profile }
|
|
5383
|
+
{ id: "apps", icon: "grid", label: T.apps }
|
|
5157
5384
|
];
|
|
5158
|
-
return /* @__PURE__ */ React.createElement("div", { style: { ...vars, "--row-pad": rowPad, position: "fixed", inset: 0, display: "flex", background: "var(--bg)", color: "var(--text)", fontFamily: "var(--ui)" } }, /* @__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("
|
|
5385
|
+
return /* @__PURE__ */ React.createElement("div", { style: { ...vars, "--row-pad": rowPad, position: "fixed", inset: 0, display: "flex", background: "var(--bg)", color: "var(--text)", fontFamily: "var(--ui)" } }, /* @__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: {
|
|
5386
|
+
padding: 4,
|
|
5387
|
+
borderRadius: 12,
|
|
5388
|
+
cursor: "pointer",
|
|
5389
|
+
display: "flex",
|
|
5390
|
+
border: "1px solid " + (tab === "profile" ? "var(--line)" : "transparent"),
|
|
5391
|
+
background: tab === "profile" ? "var(--panel-2)" : "transparent"
|
|
5392
|
+
} }, /* @__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, padding: "0 16px" } }, /* @__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)" } }, "\xB7 ", (nav.find((n) => n.id === tab) || { label: T.profile }).label.toLowerCase()), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }), /* @__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))), tab === "chat" && /* @__PURE__ */ React.createElement(ChatTab, { T, lang: t.lang, 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("") }), 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 })), data.locked && /* @__PURE__ */ React.createElement(
|
|
5159
5393
|
DkLockedOut,
|
|
5160
5394
|
{
|
|
5161
5395
|
compact: true,
|
package/package.json
CHANGED