@decentnetwork/beagle 0.1.79 → 0.1.81

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.
@@ -90,7 +90,9 @@ export declare class CarrierNode extends EventEmitter {
90
90
  isFriendOnline(userid: string): boolean;
91
91
  sessionStatus(pubkey: string): unknown;
92
92
  dhtHealth(): unknown;
93
- sendText(userid: string, text: string): Promise<void>;
93
+ sendText(userid: string, text: string): Promise<{
94
+ delivery: "acked" | "accepted" | "offline";
95
+ } | void>;
94
96
  sendFriendRequest(address: string, hello?: string, opts?: {
95
97
  force?: boolean;
96
98
  }): Promise<void>;
@@ -1,4 +1,4 @@
1
- window.__DK_UI_VERSION="0.1.78";
1
+ window.__DK_UI_VERSION="0.1.80";
2
2
  "use strict";
3
3
  (() => {
4
4
  // packages/beagle-ui/dist/beagle-ui.js
@@ -1576,7 +1576,7 @@ window.__DK_UI_VERSION="0.1.78";
1576
1576
  },
1577
1577
  /* @__PURE__ */ React.createElement(Icon, { name: "clock", size: 11, stroke: 2.2, color: "var(--faint)" }),
1578
1578
  /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 10, color: "var(--faint)" } }, m.status === "sending" ? T.sendingMsg || "sending" : T.queued || "queued")
1579
- ) : mine && m.status && /* @__PURE__ */ React.createElement(Icon, { name: "checkCheck", size: 12, stroke: 2.2, color: m.status === "read" ? "var(--accent)" : "var(--faint)" })))
1579
+ ) : 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)" })))
1580
1580
  );
1581
1581
  }
1582
1582
  function Conversation({ T, peer, lang, peers, onOpenChat, thread: threadProp, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread }) {
@@ -326,8 +326,8 @@ export class EmbeddedHost {
326
326
  // "queued" a terminal state for text: the clock icon never resolved.
327
327
  if (!msg.file) {
328
328
  try {
329
- await this.#node.sendText(pubkey, msg.text);
330
- this.#messages.setStatus(pubkey, msg.id, "sent");
329
+ const r = await this.#node.sendText(pubkey, msg.text);
330
+ this.#messages.setStatus(pubkey, msg.id, "sent", !r || r.delivery === "acked");
331
331
  this.#events.emit("event", { type: "chat", userid: pubkey, dir: "out" });
332
332
  }
333
333
  catch (e) {
@@ -583,8 +583,11 @@ export class EmbeddedHost {
583
583
  const job = prev.then(async () => {
584
584
  await this.#flushOutbox(uid).catch(() => { });
585
585
  try {
586
- await this.#node.sendText(uid, text);
587
- this.#messages.setStatus(uid, msg.id, "sent");
586
+ // "acked" is a confirmation from the far side; anything else is
587
+ // the transport taking the bytes. Still "sent" — resending on an
588
+ // unconfirmed message is how a phone ends up with two copies.
589
+ const r = await this.#node.sendText(uid, text);
590
+ this.#messages.setStatus(uid, msg.id, "sent", !r || r.delivery === "acked");
588
591
  }
589
592
  catch (e) {
590
593
  // Offline peer or failed delivery: same recovery either way —
@@ -20,6 +20,9 @@ export interface ChatMessage {
20
20
  * daemon will deliver it (in order) the moment the friend reconnects.
21
21
  * Cleared once actually sent. */
22
22
  status?: "sending" | "queued" | "sent" | "failed";
23
+ /** True when the far side confirmed receipt; false when "sent" is a guess.
24
+ * Absent on old records and on inbound messages. */
25
+ confirmed?: boolean;
23
26
  /** How this message traversed the network, for the UI to distinguish at a
24
27
  * glance (different colors). "online" = live net_crypto session (direct/relay);
25
28
  * "offline" = express store-and-forward (the friend or we were offline). Lets
@@ -58,7 +61,15 @@ export declare class MessageStore {
58
61
  /** Set (or, with undefined, clear) the delivery status on a text message.
59
62
  * Used to flip a "queued" message to delivered once it's actually sent.
60
63
  * No-op if the id isn't found or is a file entry. Returns true if patched. */
61
- setStatus(peer: string, id: string, status?: "sending" | "queued" | "sent" | "failed"): boolean;
64
+ /**
65
+ * `confirmed` distinguishes a tick that is EVIDENCE from a tick that is a
66
+ * guess. sendText reports "acked" only when the far side answered — an
67
+ * app-level ACK from a JS peer, toxcore's own ACK from a phone. Anything
68
+ * else went to a live session and was never confirmed. The status stays
69
+ * "sent" either way, because the message is not queued and must not be
70
+ * resent; the flag is what the UI reads to stop over-claiming.
71
+ */
72
+ setStatus(peer: string, id: string, status?: "sending" | "queued" | "sent" | "failed", confirmed?: boolean): boolean;
62
73
  /** Has this peer ever sent us anything? Used as proof-of-friendship for
63
74
  * records that predate the SDK's acceptedAt bookkeeping — a message can
64
75
  * only arrive over an established session. */
@@ -62,11 +62,23 @@ export class MessageStore {
62
62
  /** Set (or, with undefined, clear) the delivery status on a text message.
63
63
  * Used to flip a "queued" message to delivered once it's actually sent.
64
64
  * No-op if the id isn't found or is a file entry. Returns true if patched. */
65
- setStatus(peer, id, status) {
65
+ /**
66
+ * `confirmed` distinguishes a tick that is EVIDENCE from a tick that is a
67
+ * guess. sendText reports "acked" only when the far side answered — an
68
+ * app-level ACK from a JS peer, toxcore's own ACK from a phone. Anything
69
+ * else went to a live session and was never confirmed. The status stays
70
+ * "sent" either way, because the message is not queued and must not be
71
+ * resent; the flag is what the UI reads to stop over-claiming.
72
+ */
73
+ setStatus(peer, id, status, confirmed) {
66
74
  const arr = this.byPeer.get(peer);
67
75
  const msg = arr?.find((m) => m.id === id);
68
76
  if (!msg || msg.file)
69
77
  return false;
78
+ if (confirmed === undefined)
79
+ delete msg.confirmed;
80
+ else
81
+ msg.confirmed = confirmed;
70
82
  if (status)
71
83
  msg.status = status;
72
84
  else
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decentnetwork/beagle",
3
- "version": "0.1.79",
3
+ "version": "0.1.81",
4
4
  "description": "Beagle — P2P chat, file transfer and calls for regular users, on the Decent Network. No admin privilege required.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -27,14 +27,14 @@
27
27
  "dependencies": {
28
28
  "@decentnetwork/chat-components": "^0.1.3",
29
29
  "@decentnetwork/lan": "^0.1.283",
30
- "@decentnetwork/peer": "^0.1.160",
30
+ "@decentnetwork/peer": "^0.1.163",
31
31
  "@decentnetwork/peer-webrtc": "^0.2.16",
32
32
  "js-yaml": "^4.1.0",
33
33
  "ws": "^8.21.1",
34
34
  "yargs": "^17.7.2"
35
35
  },
36
36
  "devDependencies": {
37
- "@decentnetwork/beagle-ui": "^0.2.3",
37
+ "@decentnetwork/beagle-ui": "^0.2.4",
38
38
  "@types/js-yaml": "^4.0.9",
39
39
  "@types/node": "^20.11.0",
40
40
  "@types/yargs": "^17.0.32",