@decentnetwork/beagle 0.1.92 → 0.1.94

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.
@@ -1,4 +1,4 @@
1
- window.__DK_UI_VERSION="0.1.92";
1
+ window.__DK_UI_VERSION="0.1.94";
2
2
  "use strict";
3
3
  (() => {
4
4
  // packages/beagle-ui/dist/beagle-ui.js
package/dist/server.js CHANGED
@@ -75,6 +75,50 @@ async function ensFetchJson(path) {
75
75
  clearTimeout(timer);
76
76
  }
77
77
  }
78
+ /** requestedAt / acceptedAt per friend, read from the SDK's own friend store
79
+ * (`<keyFile>.friends.json`, next to the identity). The daemon's friends-list
80
+ * reports neither for a request we sent and only acceptedAt (as lastSeen)
81
+ * otherwise, so a friend just added had no stamp at all and sorted to the
82
+ * bottom — the third time this was reported. The store is the source of
83
+ * truth both backends write, and it is a file in our own config dir, so
84
+ * read it. Cached by mtime: the list is polled every 2.5s per tab. */
85
+ let friendStampsCache = null;
86
+ function friendStoreStamps(keyFile) {
87
+ const empty = new Map();
88
+ if (!keyFile)
89
+ return empty;
90
+ const path = `${keyFile}.friends.json`;
91
+ try {
92
+ const st = statSync(path);
93
+ if (friendStampsCache && friendStampsCache.mtimeMs === st.mtimeMs)
94
+ return friendStampsCache.map;
95
+ const raw = JSON.parse(readFileSync(path, "utf-8"));
96
+ const recs = Array.isArray(raw)
97
+ ? raw
98
+ : raw && typeof raw === "object" && Array.isArray(raw.friends)
99
+ ? raw.friends
100
+ : Object.values(raw ?? {});
101
+ const map = new Map();
102
+ for (const r of recs) {
103
+ if (!r || typeof r !== "object")
104
+ continue;
105
+ const id = r.userid || r.pubkey;
106
+ if (!id)
107
+ continue;
108
+ map.set(id, {
109
+ requestedAt: typeof r.requestedAt === "number" ? r.requestedAt : undefined,
110
+ acceptedAt: typeof r.acceptedAt === "number" ? r.acceptedAt : undefined,
111
+ });
112
+ }
113
+ friendStampsCache = { mtimeMs: st.mtimeMs, map };
114
+ return map;
115
+ }
116
+ catch {
117
+ // Unreadable or absent (a fresh install has no friends yet): whatever we
118
+ // last read, else nothing — the list still renders, just without stamps.
119
+ return friendStampsCache?.map ?? empty;
120
+ }
121
+ }
78
122
  /** The record beagle writes for its own name: what the profile page shows
79
123
  * (displayName, description) on top of whatever the record already held —
80
124
  * wallet binds, socials, avatar, referee — so a profile edit never strips a
@@ -1966,10 +2010,13 @@ export function startBeagleServer(opts) {
1966
2010
  beagleVer: beagleVersion(),
1967
2011
  };
1968
2012
  const fl = (flist.ok ? (flist.data?.friends ?? []) : []);
2013
+ const stamps = friendStoreStamps(opts.callIce?.keyFile);
1969
2014
  const peers = fl.map((f) => {
1970
2015
  const uid = f.userid ?? "";
1971
2016
  const sess = sessByUserid.get(uid);
1972
2017
  const status = f.status;
2018
+ const stamp = stamps.get(uid);
2019
+ const acceptedAt = f.lastSeen ?? stamp?.acceptedAt ?? 0;
1973
2020
  const online = status === "connected" || status === "online";
1974
2021
  const realName = typeof f.name === "string" && f.name !== uid ? f.name : undefined;
1975
2022
  const lm = f.lastMessage;
@@ -1982,10 +2029,14 @@ export function startBeagleServer(opts) {
1982
2029
  address: f.address || "",
1983
2030
  online,
1984
2031
  // Friendship not yet accepted by the other side. Only trusted when
1985
- // the backend states it explicitly (embedded does); a daemon
1986
- // backend that doesn't report it defaults to established, so the
1987
- // banner can never wrongly appear on an old backend.
1988
- pending: f.accepted === false,
2032
+ // the backend states it explicitly: the embedded host's `accepted`,
2033
+ // or — on the daemon — SDK status "requested" WITH no acceptedAt.
2034
+ // Status alone is not enough: the daemon keeps "requested" on a
2035
+ // friend who accepted but has never connected since, and reading
2036
+ // it as pending put the waiting banner on 22 of 65 friends.
2037
+ // Anything else defaults to established, so the banner can never
2038
+ // wrongly appear on an old backend.
2039
+ pending: f.accepted === false || (status === "requested" && !acceptedAt),
1989
2040
  via: online ? viaFromTransport(sess?.transport) : null,
1990
2041
  ping: null,
1991
2042
  ip: ipByUserid.get(uid) ?? "",
@@ -2005,11 +2056,13 @@ export function startBeagleServer(opts) {
2005
2056
  // The sidebar ranks a friend by the newest of these and the last
2006
2057
  // message, so someone you just accepted — or just added — lands
2007
2058
  // at the top like a fresh message would, not in a no-history
2008
- // group at the bottom. A daemon backend reports only lastSeen
2009
- // (its acceptedAt); requestedAt is embedded-only and reads 0
2010
- // elsewhere.
2011
- acceptedAt: f.lastSeen ?? 0,
2012
- requestedAt: f.requestedAt ?? 0,
2059
+ // group at the bottom. The backend's own fields first; the SDK's
2060
+ // friend store on disk fills what the backend does not report — a
2061
+ // daemon backend gives only lastSeen (its acceptedAt) and nothing
2062
+ // for a request we sent, which left a just-added friend with no
2063
+ // stamp at all.
2064
+ acceptedAt,
2065
+ requestedAt: f.requestedAt ?? stamp?.requestedAt ?? 0,
2013
2066
  wire: "163",
2014
2067
  avatarUrl: ensPub.get(uid)?.avatarUrl ?? null,
2015
2068
  // What they advertised over Carrier wins over the directory: it is
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decentnetwork/beagle",
3
- "version": "0.1.92",
3
+ "version": "0.1.94",
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",