Skip to content

Inbox row disappears when clicked instead of staying as read #106

@iduartgomez

Description

@iduartgomez

Symptom

User reported during manual cross-node testing post-#102 / #104:

issue detected, whenever i receive new messages on 7510 and i click on them they disappear...

Click on an inbox row → opens the message detail (read flow) → after closing the detail (or auto-close after `mark_as_read`) the row is missing from the inbox list.

Expected

Read messages stay in the inbox list, marked as read (faded), until explicit Archive/Delete. The kept-locally copy in `mail-local-state` is supposed to survive the contract-side eviction so the row persists locally.

Code path

`OpenMessage` in `ui/src/app.rs`:

  • Calls `InboxView::mark_as_read` → `InboxView::remove_messages` → `InboxModel::remove_messages` (strips from in-memory `messages` Vec + sends `UpdateInbox::RemoveMessages` delta to the contract).
  • Calls `local_state::local_mark_read(alias, msg.id, KeptMessage{...})` which inserts into the per-alias `kept` HashMap and `bump()`s the GENERATION counter.

`MessageList` rebuild (`app.rs:1140`):

  1. Iterates `current_model.messages` (now empty for this id after remove_messages).
  2. Iterates `kept_for(alias)` and pushes the kept entry (with `sender_vk: Vec::new()`, `signature_valid: false`).
  3. Filters: `is_archived` (no), `is_deleted` (no), `matches_search` (yes for empty), `hide_unsigned` (default false), `quarantine_unknown` (default false).

So the kept row should appear. It doesn't in the bug repro.

Hypotheses

  • GENERATION is `AtomicUsize` not a Signal, so `bump()` doesn't trigger a Dioxus re-render. Components that touch GENERATION via `load(..)` only see the new value on the next render that fires for some other reason. `MessageList` reads `GENERATION` (line 1216) but a single `load` in setup doesn't subscribe to changes.
  • id mismatch: `KeptMessage` keyed by `MessageId.to_string()` where MessageId is the in-memory `next_msg_id` (monotonic per session, not globally stable). After remove + a follow-up GetResponse rebuilds the model from contract state, the `next_msg_id` resets and ids may drift from the kept entry.
  • Render order: `MessageList`'s rebuild loop only runs inside the `if let Some((current_model, id)) = ...find_map(...)` arm. If the active identity is not found in `InboxesData` after a full state rebuild, the kept-merge branch never runs.

Repro plan

  1. Iso 2-node, `FREENET_LIVE_E2E_SEND=1` doesn't matter — local single-node `cargo make publish-email-test` against `run-node` is enough.
  2. Two browsers, alice + bob, send alice → bob.
  3. Bob receives row.
  4. Bob clicks row → detail opens.
  5. Bob navigates back → row gone.

Test gap

`live-node.spec.ts` `multi-round + read + archive across nodes` (added in #104, gated) covers this with the assertion `expect(bobApp.getByText(/round one/i)).toBeVisible({ timeout: 10_000 })` after navigating back.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions