From ccb70fec35e8d88f08cd2a884ed57af6c2ab8065 Mon Sep 17 00:00:00 2001 From: jolavillette Date: Tue, 18 Aug 2026 10:06:21 +0200 Subject: [PATCH] webui: the profile pane read two core fields the way they are not written "Type" tested `mFlags === 14` against a bitfield. 14 is PGP_LINKED | PGP_KNOWN | IS_OWN_ID, so the only identities that ever matched were our own signed ones: a friend's signed identity carries 6, or 7 with the contact bit, and was labelled "Anonymous ID". The bit to look at is RS_IDENTITY_FLAGS_PGP_LINKED, 0x2. "Usage Statistics" mapped mServiceId over 1..8, but that field is an RsServiceType -- 0x0211 for the identities, 0x0215 for the forums, 0x0217 for the channels, 0x0012 for the chat. No case could match, so every line of the panel came out as "Unknown (533)". The usage codes next to it are right, they really do run 0 to 21; it is the service that was being read from another table. The two timestamps read `.xint64` straight out of the field instead of going through get64Num, which exists for exactly that: a 64 bit value arrives as {xint64, xstr64} and large ones carry the string alone, where the pane then shows "Invalid Date". Details were also fetched once and kept for the whole session -- deliberately, it is what makes the lists cheap -- which froze the reputation, the usage record and the avatar of the profile being *looked at*. That one is now asked again after a minute, over the top of what is displayed rather than by clearing it. Co-Authored-By: Claude Opus 5 (1M context) --- webui-src/app/people/people_details_tab.js | 31 +++++++++++-------- webui-src/app/people/people_state.js | 36 +++++++++++++++------- 2 files changed, 43 insertions(+), 24 deletions(-) diff --git a/webui-src/app/people/people_details_tab.js b/webui-src/app/people/people_details_tab.js index 4d56020..d635516 100644 --- a/webui-src/app/people/people_details_tab.js +++ b/webui-src/app/people/people_details_tab.js @@ -7,6 +7,7 @@ const { EditIdentity, DeleteIdentity } = ownIdsLayout; const { State, fetchIdDetails, + refreshSelectedIdDetails, getSafeAvatar, get64Num, createUsageString, @@ -16,6 +17,7 @@ const { const DetailsTab = () => { return { + oninit: () => refreshSelectedIdDetails(), view: () => { fetchIdDetails(State.selectedId); const details = State.selectedId ? State.gxsIdToDetailsMap[State.selectedId] : null; @@ -164,23 +166,26 @@ const DetailsTab = () => { m('.info-label', 'GXS ID'), m('.info-value', details.mId), m('.info-label', 'Type'), - m('.info-value', details.mFlags === 14 ? 'Signed ID' : 'Anonymous ID'), + // mFlags is a bitfield: RS_IDENTITY_FLAGS_PGP_LINKED is 0x2. + // Comparing the whole word against 14 -- PGP_LINKED | PGP_KNOWN | + // IS_OWN_ID -- only ever matched our own signed identities, so + // every signed identity of somebody else read "Anonymous". + m('.info-value', (details.mFlags & 0x2) ? 'Signed ID' : 'Anonymous ID'), m('.info-label', 'Owner Node GPG'), m('.info-value', pgpId && pgpId !== '0000000000000000' ? pgpId : 'None'), m('.info-label', 'Created On'), - m( - '.info-value', - typeof details.mPublishTS === 'object' - ? new Date(details.mPublishTS.xint64 * 1000).toLocaleString() - : 'Unknown' - ), + // get64Num exists for these: a 64 bit field arrives as + // {xint64, xstr64}, and large values carry xstr64 alone -- reading + // .xint64 straight then dates the identity to "Invalid Date". + m('.info-value', (() => { + const ts = get64Num(details.mPublishTS); + return ts > 0 ? new Date(ts * 1000).toLocaleString() : 'Unknown'; + })()), m('.info-label', 'Last Used'), - m( - '.info-value', - typeof details.mLastUsageTS === 'object' - ? new Date(details.mLastUsageTS.xint64 * 1000).toLocaleDateString() - : 'Unknown' - ), + m('.info-value', (() => { + const ts = get64Num(details.mLastUsageTS); + return ts > 0 ? new Date(ts * 1000).toLocaleDateString() : 'Unknown'; + })()), m('.info-label', 'Friend votes'), m('.info-value', details.mReputation && (details.mReputation.mFriendsPositiveVotes > 0 || details.mReputation.mFriendsNegativeVotes > 0) ? `${details.mReputation.mFriendsPositiveVotes} positive, ${details.mReputation.mFriendsNegativeVotes} negative` diff --git a/webui-src/app/people/people_state.js b/webui-src/app/people/people_state.js index efdd795..bce437f 100644 --- a/webui-src/app/people/people_state.js +++ b/webui-src/app/people/people_state.js @@ -185,18 +185,32 @@ function get64Num(val) { return Number(val) || 0; } +// RsIdentityUsage::mServiceId is an RsServiceType (rsserviceids.h), a 16 bit +// service number -- 0x0215 for the forums, 0x0217 for the channels. Matching it +// against 1..8 could never succeed, so every line of the usage panel used to +// read "Unknown (533)". +const SERVICE_NAMES = { + 0x0012: 'Chat', + 0x0022: 'Mail', + 0x0023: 'Direct mail', + 0x0024: 'Distant mail', + 0x0027: 'Distant chat', + 0x0028: 'GXS tunnels', + 0x0211: 'Identities', + 0x0213: 'Wiki', + 0x0214: 'Wire', + 0x0215: 'Forums', + 0x0216: 'Boards', + 0x0217: 'Channels', + 0x0218: 'Circles', + 0x0219: 'Reputation', + 0x0221: 'Calendar', + 0x0230: 'Distant messages', +}; + function getServiceName(serviceId) { - switch (serviceId) { - case 1: return 'Channels'; - case 2: return 'Forums'; - case 3: return 'Boards'; - case 4: return 'Chat'; - case 5: return 'GxsCircles'; - case 6: return 'GxsMail'; - case 7: return 'GxsCircles'; - case 8: return 'Wire'; - default: return 'Unknown (' + serviceId + ')'; - } + const id = Number(serviceId); + return SERVICE_NAMES[id] || ('Unknown (0x' + id.toString(16) + ')'); } function createUsageString(u) {