@decentnetwork/lan 0.1.283 → 0.1.284

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.
@@ -393,7 +393,11 @@ export class DaemonServer {
393
393
  this.ipcEvents.setMaxListeners(50);
394
394
  this.ipcServer = new IpcServer(ipcSocketPath(this.config.carrier.dataDir), {
395
395
  friendRequest: async (address, hello) => {
396
- await this.peerManager.sendFriendRequest(address, hello);
396
+ // sendFriendRequest waits for our own announce to store and then
397
+ // walks the DHT toward the target — tens of seconds is normal, and
398
+ // none of it changes what the caller should do. Awaiting it made
399
+ // "add" look like a dead button for the whole walk.
400
+ void this.peerManager.sendFriendRequest(address, hello).catch((e) => this.logger.warn(`friend-request to ${String(address).slice(0, 8)} failed: ${e.message}`));
397
401
  },
398
402
  friendsPending: async () => {
399
403
  const entries = this.pendingFriends?.list() ?? [];
@@ -412,7 +416,14 @@ export class DaemonServer {
412
416
  const entry = this.pendingFriends?.remove(userid);
413
417
  if (!entry)
414
418
  throw new Error(`No pending friend-request for userid ${userid}`);
415
- await this.peerManager.acceptFriendRequest(entry.pubkey);
419
+ // Answer as soon as the entry is gone from pending — that is the part
420
+ // the UI renders. The node call is effectively local (its session
421
+ // attempt is already fire-and-forget), so waiting on it only delayed
422
+ // the click. Put the entry back if it does refuse.
423
+ void this.peerManager.acceptFriendRequest(entry.pubkey).catch((e) => {
424
+ this.pendingFriends?.add(entry);
425
+ this.logger.warn(`accept ${userid.slice(0, 8)} failed: ${e.message}`);
426
+ });
416
427
  },
417
428
  friendsReject: async (userid) => {
418
429
  const entry = this.pendingFriends?.remove(userid);
@@ -423,23 +434,37 @@ export class DaemonServer {
423
434
  chatSend: async (userid, text) => {
424
435
  if (!text)
425
436
  return;
426
- // If the friend is online, deliver immediately. If they're offline (or
427
- // the live send throws), queue it: store as "queued" and flush on their
428
- // next reconnect — so chat works store-and-forward, not just live.
429
- if (this.peerManager?.isFriendOnline(userid)) {
437
+ // Record the message BEFORE any network attempt, and return without
438
+ // waiting for the wire.
439
+ //
440
+ // The offline branch already did this. The ONLINE branch awaited
441
+ // sendText and only logged afterwards — and sendText is not quick:
442
+ // with the ack path it retries for up to 15s. So a message to an
443
+ // ONLINE friend did not appear in the sender's own thread until
444
+ // delivery finished, while a message to an offline one appeared at
445
+ // once. The faster the peer, the less you noticed; the slower, the
446
+ // more it looked like the app had ignored you.
447
+ //
448
+ // Same rule the embedded host and the browser client already follow:
449
+ // a delivery failure is a STATE on the message, never its absence.
450
+ const msg = this.messageStore?.append(userid, "out", text, Date.now(), "queued");
451
+ this.friendMeta?.ensure(userid);
452
+ this.ipcEvents.emit("event", { type: "chat", userid, dir: "out" });
453
+ if (!msg || !this.peerManager?.isFriendOnline(userid)) {
454
+ this.logger.info(`Queued text for ${userid.slice(0, 8)} (delivers on reconnect)`);
455
+ return;
456
+ }
457
+ // Deliver in the background; the reconnect flush covers a failure.
458
+ void (async () => {
430
459
  try {
431
460
  await this.peerManager.sendText(userid, text);
432
- this.logChat(userid, "out", text);
433
- return;
461
+ this.messageStore?.setStatus(userid, msg.id, undefined); // delivered
434
462
  }
435
463
  catch (e) {
436
- this.logger.warn(`sendText to ${userid.slice(0, 8)} failed, queuing: ${e.message}`);
464
+ this.logger.warn(`sendText to ${userid.slice(0, 8)} failed, stays queued: ${e.message}`);
437
465
  }
438
- }
439
- this.messageStore?.append(userid, "out", text, Date.now(), "queued");
440
- this.friendMeta?.ensure(userid);
441
- this.ipcEvents.emit("event", { type: "chat", userid, dir: "out" });
442
- this.logger.info(`Queued text for offline ${userid.slice(0, 8)} (delivers on reconnect)`);
466
+ this.ipcEvents.emit("event", { type: "chat", userid, dir: "out" });
467
+ })();
443
468
  },
444
469
  chatLogLocal: async (userid, dir, text) => {
445
470
  if (!text)
@@ -1,4 +1,4 @@
1
- window.__DK_UI_VERSION="0.1.283";
1
+ window.__DK_UI_VERSION="0.1.284";
2
2
  const ICON_PATHS = {
3
3
  // ---- tab bar (the four must feel like one set) ----
4
4
  users: '<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/>',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decentnetwork/lan",
3
- "version": "0.1.283",
3
+ "version": "0.1.284",
4
4
  "description": "Private virtual LAN for self-hosted services and AI agents, built on Elastos Carrier. NAT-traversal, name service, ACL, all over a peer-to-peer mesh — no public IP required.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",