fix(ui): reload state loss, 2nd-send freeze, folder counts + text size (#265, #267, #268)#269
Merged
Merged
Conversation
… echo (#265) A stale GetAll echo from the mail-local-state delegate wholesale-overwrites the SNAPSHOT, wiping optimistic writes that haven't round-tripped yet. Drafts (#107) and kept rows (#113) were already protected by tombstones, but the Sent stash, delete tombstones, and archive snapshots had none — so a reload race dropped them. Symptom (beta tester): Sent messages reappear as Drafts and deleted messages return after leaving and re-entering the app. replace_snapshot now re-merges OPTIMISTIC_SENT, OPTIMISTIC_ARCHIVED, and DELETED_MESSAGES on every echo until the delegate confirms the entry, mirroring the existing draft/kept pattern. Each protection drops once the echo includes the persisted state, so tombstones don't leak. Tests: 3 reproduce the clobber (sent/deleted/archived), 3 pin the confirm-then-drop lifecycle. Note: this closes the in-session echo race only. A delegate write that fails outright (no persistent retry) can still lose state across a real reload — tracked separately.
DecryptedMessage::from_stored .expect()-panicked when ml_kem_decrypt or the plaintext deserialize failed. An undecryptable message in shared inbox state (encrypted for a different identity, or malformed/foreign in a multi-node mesh) therefore aborted the wasm module and froze the entire UI. Symptom (beta tester): sending a second message shortly after the first freezes the UI with a WASM error in the console — the second send's UPDATE round-trip surfaced a delta carrying a message this identity couldn't decrypt. from_stored now returns Option and logs+skips on failure. from_state filters out the undecryptable entries (filter_map) and apply_delta continues past them. An undecryptable message in shared state must never be fatal. Tests: from_stored returns None on garbage + on a message encrypted for another identity; from_state keeps decryptable rows and drops the foreign one.
…268) Two beta-tester UI complaints. A) Folder counts don't match contents. The sidebar badge for Inbox/Quarantine counted unread rows without excluding deleted/archived/hide_unsigned messages, while the MessageList render filter excludes them. The badge then exceeded the visible row count. folder_count now applies the same exclusions via a shared is_inbox_row_visible predicate (search intentionally not applied — the badge reflects the folder, not the search box). Tests assert the badge drops when an unread message is deleted or archived. B) Text too small. ~131 hardcoded sub-15px font-sizes bumped across the component CSS (7.5px->9px ... 14.5px->15px). Display sizes >=16px left as-is.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Beta-tester bug sweep from the testing feedback (5-node manual testing). Fixes three of the four reported issues; #266 is analyzed but deferred (architectural, see below).
Fixes
#265 — Sent→Draft / deleted messages return on reload
A stale
GetAllecho from themail-local-statedelegate wholesale-overwrites the in-memorySNAPSHOT, wiping optimistic writes that haven't round-tripped. Drafts (#107) and kept rows (#113) were already tombstone-protected; the Sent stash, delete tombstones, and archive snapshots were not.replace_snapshotnow re-mergesOPTIMISTIC_SENT/OPTIMISTIC_ARCHIVED/DELETED_MESSAGESuntil the delegate confirms each, then drops the protection.Scope note: closes the in-session echo race. A delegate write that fails outright (fire-and-forget
spawn, no persistent retry) can still lose state across a real reload — separate follow-up.#267 — UI freeze + WASM error on second send
DecryptedMessage::from_stored.expect()-panicked on decrypt/deserialize failure, aborting the wasm module. An undecryptable message in shared inbox state (foreign/malformed, common in a multi-node mesh) froze the whole UI on the UPDATE that followed a second send. Now returnsOption;from_statefilters andapply_deltaskips. An undecryptable message is never fatal.#268 — Folder counts ≠ contents; text too small
folder_countnow sharesis_inbox_row_visiblewith the render filter.Not in this PR — #266 (token pop-ups despite toggles)
Root-caused (see #266 comment):
allow_verified_skip_tokenonly changes recipient contract policy; the sender path always mints + prompts. The sender-side skip was never wired intostart_sending/assign_token(the #157 design note oversold it). Needs the sender to read the recipient's advertised bypass flag and skip minting — deferred to a dedicated PR.Testing
use-noderelease build compiles.create identity → reload persists → permission flow✓ andalice → bob cross-node send + receive (#81)✓. The other 4 specs failed on a pre-existing freenet-core cross-node flake (missing contract parameterswedged the gateway → ECONNREFUSED cascade), not these changes.🤖 Generated with Claude Code