feat: show outbound interface in message details (#646)#666
feat: show outbound interface in message details (#646)#666torlando-tech merged 11 commits intomainfrom
Conversation
app/src/main/java/com/lxmf/messenger/viewmodel/MessagingViewModel.kt
Outdated
Show resolved
Hide resolved
Greptile SummaryThis PR adds end-to-end tracking of the outbound network interface for sent messages, surfaced as a "Sent Via" card in the Message Details screen. It plumbs a new
Confidence Score: 4/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant UI as MessageDetailScreen
participant VM as MessagingViewModel
participant RP as ReticulumProtocol (AIDL)
participant PW as PythonWrapperManager
participant PY as rns_api.py
participant DB as Room DB (messages)
Note over VM: Send path
VM->>RP: getNextHopInterfaceName(receipt.destinationHash)
RP->>PW: getNextHopInterfaceName(destHash)
PW->>PY: rnsApi.get_next_hop_interface_name(dest_hash)
PY->>PY: RNS.Transport.has_path(dest_hash)
PY->>PY: RNS.Transport.next_hop_interface(dest_hash)
PY->>PY: format_interface_name(iface)
PY-->>PW: "TCPInterface[Server/1.2.3.4:4242]" or None
PW-->>RP: String?
RP-->>VM: sentInterface?
VM->>DB: insertMessage(... sentInterface=sentInterface)
Note over VM: Delivery enrichment path (status == "delivered" && sentInterface == null)
VM->>VM: enrichSentInterfaceOnDelivery(message, messageHash)
VM->>RP: getNextHopInterfaceName(conversationHash bytes)
RP-->>VM: sentInterface?
VM->>DB: updateSentInterface(messageHash, sentInterface)
Note over UI: Display
DB-->>VM: MessageEntity (sentInterface field)
VM->>UI: MessageUi.sentInterface
UI->>UI: Show "Sent Via" card (if non-null)
|
app/src/main/java/com/lxmf/messenger/service/manager/RoutingManager.kt
Outdated
Show resolved
Hide resolved
app/src/main/java/com/lxmf/messenger/viewmodel/MessagingViewModel.kt
Outdated
Show resolved
Hide resolved
| conversationLinkManager.recordPeerActivity(message.conversationHash, update.timestamp) | ||
| } | ||
|
|
||
| // Enrich sentInterface on delivery if it wasn't captured at send time | ||
| enrichSentInterfaceOnDelivery(message, update.messageHash) | ||
|
|
||
| // Trigger refresh to ensure UI updates (Room invalidation doesn't always propagate with cachedIn) | ||
| _messagesRefreshTrigger.value++ | ||
|
|
There was a problem hiding this comment.
Bug: The enrichSentInterfaceOnDelivery function is called with a stale MessageEntity, causing the check for deliveryMethod == "propagated" to fail and apply incorrect logic.
Severity: MEDIUM
Suggested Fix
After updating the message in the database within handleDeliveryStatusUpdate, refetch the MessageEntity from the conversationRepository before calling enrichSentInterfaceOnDelivery. This will ensure the function operates on the most recent version of the message data.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.
Location: app/src/main/java/com/lxmf/messenger/viewmodel/MessagingViewModel.kt#L902-L910
Potential issue: In `handleDeliveryStatusUpdate`, a `MessageEntity` is fetched before
its `deliveryMethod` is updated to "propagated" in the database. This stale message
object is then passed to `enrichSentInterfaceOnDelivery`. Inside this function, a check
`message.deliveryMethod == "propagated"` fails because it's evaluating the old, stale
value. This causes the function to incorrectly proceed with interface enrichment for
propagated messages, which can result in associating the wrong `sentInterface` with the
message or leaving it as null, as the logic queries with the incorrect
`conversationHash` for this message type.
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
app/src/main/java/com/lxmf/messenger/viewmodel/MessagingViewModel.kt
Outdated
Show resolved
Hide resolved
1 similar comment
Track which network interface sent messages go through and display it as a "Sent Via" card in the Message Details screen. Creates python/rns_api.py as the Strangler Fig Phase 0 bootstrap alongside reticulum_wrapper.py. - Add sentInterface column to messages table (Room migration 42→43) - Create rns_api.py with thin RNS.Transport.next_hop_interface() passthrough - Plumb getNextHopInterfaceName through AIDL IPC stack - Query interface at send time, enrich on delivery callback if still null - Rename ReceivingInterfaceInfo → InterfaceInfo for bidirectional use - Display "Sent Via" card for sent messages in MessageDetailScreen Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Propagated sends route through the propagation node (a different destination hash than conversationHash), so querying conversationHash in the delivery callback would return the wrong interface or null. The send-time capture already uses receipt.destinationHash which handles propagated sends correctly. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Extract sentInterface enrichment into helper method to reduce NestedBlockDepth in handleDeliveryStatusUpdate - Remove unused identityResolutionManager constructor parameter Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Log exceptions in rns_api.py instead of silently swallowing - Guard enrichSentInterfaceOnDelivery against retrying_propagated status to prevent stale message state from bypassing the propagation check Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…t to delivered - Remove unreachable get_active_propagation_node_hash from rns_api.py and its unused router/reticulum init plumbing - Only enrich sentInterface on "delivered" status — other statuses (failed, retrying_propagated) have too much routing ambiguity Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Kotlin ByteArray arrives as a Chaquopy jarray('b'), not Python bytes.
RNS Transport dict key lookups (has_path, next_hop_interface) require
bytes keys — jarray silently mismatches and returns False/None.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
514d8ba to
7900279
Compare
| suspend fun updateMessageSentInterface( | ||
| messageId: String, | ||
| sentInterface: String?, | ||
| ) { |
There was a problem hiding this comment.
Bug: The message update on delivery confirmation can silently fail if the user's active identity changes, due to an identityHash mismatch in the database query.
Severity: MEDIUM
Suggested Fix
Modify the message lookup during delivery confirmation to not rely on the current active identity. Either remove the identityHash filter from the database query to find the message across all identities, or ensure the original sender's identityHash is available and used during the update.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.
Location:
data/src/main/java/com/lxmf/messenger/data/repository/ConversationRepository.kt#L594
Potential issue: In `enrichSentInterfaceOnDelivery`, the logic to update a message's
`sentInterface` uses the currently active identity's hash (`identityHash`) for the
database query. If a user switches their active identity between the time a message is
sent and when its delivery confirmation is received, this will cause a mismatch. The
`UPDATE` query's `WHERE` clause will fail to find the message, as it was stored with the
original identity's hash. This causes the update to fail silently, and the 'Sent Via'
information for that message will not be displayed in the UI.
Replace inline class-name-only extraction with format_interface_name() for incoming messages. Previously stored just "TCPInterface" which the UI mapped to generic "TCP/IP"; now stores "TCPInterface[Beleth RNS Hub]" so the UI shows the specific server name, matching outbound behavior. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…erface_name format_interface_name returns type(obj).__name__ verbatim, so AutoInterfacePeer stays AutoInterfacePeer — not stripped to AutoInterface. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary
python/rns_api.pyas the Strangler Fig Phase 0 bootstrap — the first thin Python API file alongside the legacyreticulum_wrapper.py, with just 2 pass-through methodssentInterfacecolumn to the messages table via Room migration 42→43getNextHopInterfaceName()through the full AIDL IPC stack (PythonWrapperManager → RoutingManager → ReticulumServiceBinder → AIDL → ServiceReticulumProtocol)ReceivingInterfaceInfo→InterfaceInfofor bidirectional use (sent + received)Architecture
Test plan
:app:compileNoSentryDebugKotlinpassesInterfaceInfoTestpasses (renamed fromReceivingInterfaceInfoTest)MessageDetailViewModelTestpasses withsentInterfacemappingsentInterfacefor old messages)🤖 Generated with Claude Code