package com.sptmobile.browse import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow /** * Human labels for the phone's paired gateways, keyed by the host's **paired * node key** (the device-link iroh key in `PairedHost.node`, ADR-0002) — which * is NOT the host's spt node. The label the user wants (`mobile-gw @ HFENDULEAM`) * is the spt-node the gateway actually runs on, so it can't be looked up by the * device-link key directly. * * [resolve] bridges the two: in a host's own `listEndpoints` reply, the gateway * endpoint (id == the host's endpoint) appears on the host's spt node, carrying * that node's `node_label`. We map device-link key → that spt-node label (and * spt node key, for home-first ordering). The GUI shows labels, never hashes. */ object NodeLabels { /** paired (device-link) node key → its gateway's spt-node label. */ private val _labels = MutableStateFlow>(emptyMap()) val labels: StateFlow> = _labels /** paired (device-link) node key → the spt node its gateway runs on. */ private val _sptNodes = MutableStateFlow>(emptyMap()) val sptNodes: StateFlow> = _sptNodes /** * Learn a paired host's spt-node label + key from its OWN listing. [reply] * is that host's `listEndpoints` JSON; [hostEndpoint] its gateway endpoint * id (e.g. `mobile-gw`); [pairedNode] the device-link key it's keyed by. */ fun resolve(pairedNode: String, hostEndpoint: String, reply: String) { val ep = try { EndpointDirectory.parseHostReply(reply).asSequence() .flatMap { it.endpoints.asSequence() } .firstOrNull { it.id == hostEndpoint && it.node_label != null } } catch (_: IllegalArgumentException) { null } ?: return val label = ep.node_label ?: return if (_labels.value[pairedNode] != label) { _labels.value = _labels.value + (pairedNode to label) } if (_sptNodes.value[pairedNode] != ep.node) { _sptNodes.value = _sptNodes.value + (pairedNode to ep.node) } } }