// What the notification says about the node — the app's only always-visible surface.
//
// Everything else in favela requires opening the app. This line sits in the notification shade for as
// long as the node is alive, which makes it the one place a user learns what their phone is doing
// without asking. It is also, structurally, the honest liveness signal: the worklet lives in this
// process, so if the node dies the notification goes with it. Its PRESENCE means the node is up.
//
// ⛔ SAME RULE AS EVERY OTHER CLAIM IN THIS UI: never say more than is known. "Replicating in the
// background" was true of the service and told the user nothing about the node — on 2026-08-18 it read
// exactly the same while the node served one identity its owner thought was two, and while a post went
// out as the wrong one. A surface that reads identically in the healthy and the wrong case carries no
// information.
//
// Pure and dependency-free so it can be tested without a handset (test/describe.test.mjs), like
// lib/ipc-client.js and lib/lifecycle.js.
// A did:plc rendered for humans. Same shape favela-web uses, duplicated rather than imported because
// this module must stay dependency-free — 32 characters of base32 in a notification is decoration.
function shortDid (did) {
const d = String(did || '')
const id = d.startsWith('did:plc:') ? d.slice('did:plc:'.length) : d
return id.length > 14 ? id.slice(0, 8) + '…' + id.slice(-4) : id
}
/**
* One line describing what this node is serving, from `hostedDids().dids`.
*
* @param {Array<{did: string, handle: ?string, signing: boolean}>} dids
* @returns {string}
*/
export function describeNode (dids) {
const list = Array.isArray(dids) ? dids.filter(Boolean) : []
// No identity is not "replicating quietly" — it is a node with nothing of its own to replicate and no
// way to write. Saying so is how a fresh install stops looking like a working one.
if (!list.length) return 'Read-only — not paired to an identity'
// `signing` is whether the DID's StateBase admitted THIS device. False means the node is up, is
// replicating, and will refuse every write — the state that looks perfectly healthy until you post.
const canWrite = list.some((d) => d && d.signing)
const suffix = canWrite ? '' : ' — read-only'
if (list.length === 1) {
const d = list[0] || {}
// WHICH identity, not how many: with one DID the number carries nothing and the name is the fact
// that was got wrong.
return 'Serving ' + (d.handle || shortDid(d.did)) + suffix
}
return 'Serving ' + list.length + ' identities' + suffix
}