Problem
PR #265 (issue #261) implemented the local-only hide-stale-DM-threads filter for the left rail. The original issue marked a "Hidden conversations" sub-menu as optional for v1, so it was deferred — but it should be tracked.
Today, a thread can be un-hidden only by:
- Sending an outbound DM to the peer (calls
unhide_dm_thread via the Codex P1 fix).
- Receiving a new inbound DM from the peer (strict
> on last_any_ts revives the thread via the filter).
There is no UI path to un-hide a thread if the user just changed their mind. The hidden entry persists across reloads (it rides OUTBOUND_DMS_STORAGE_KEY) and across devices, so a misclick on Hide is recoverable only via one of the two implicit paths above.
Approach
The infrastructure is already in place:
unhide_dm_thread(room_owner_vk, peer) in ui/src/components/app/chat_delegate.rs is implemented, tombstoned-against-late-hydration (Codex P3), and unit-tested.
HIDDEN_DM_THREADS is a GlobalSignal<HashMap<(VerifyingKey, MemberId), HiddenDmThreadEntry>> so a submenu can iterate it directly.
- The
HiddenDmThreadEntry carries room_owner_vk + peer + hidden_at_ts. Resolving the room display name + peer nickname goes through the same path DmRailSection uses.
Suggested UX (open to revision):
- Settings → "Hidden conversations" panel (or kebab menu in the DM rail header).
- One row per hidden entry, showing room name + peer nickname + relative "hidden N days ago".
- Click "Show again" →
unhide_dm_thread(...) and the thread reappears in the rail.
Scope notes
References
[AI-assisted - Claude]
Problem
PR #265 (issue #261) implemented the local-only hide-stale-DM-threads filter for the left rail. The original issue marked a "Hidden conversations" sub-menu as optional for v1, so it was deferred — but it should be tracked.
Today, a thread can be un-hidden only by:
unhide_dm_threadvia the Codex P1 fix).>onlast_any_tsrevives the thread via the filter).There is no UI path to un-hide a thread if the user just changed their mind. The hidden entry persists across reloads (it rides
OUTBOUND_DMS_STORAGE_KEY) and across devices, so a misclick on Hide is recoverable only via one of the two implicit paths above.Approach
The infrastructure is already in place:
unhide_dm_thread(room_owner_vk, peer)inui/src/components/app/chat_delegate.rsis implemented, tombstoned-against-late-hydration (Codex P3), and unit-tested.HIDDEN_DM_THREADSis aGlobalSignal<HashMap<(VerifyingKey, MemberId), HiddenDmThreadEntry>>so a submenu can iterate it directly.HiddenDmThreadEntrycarriesroom_owner_vk + peer + hidden_at_ts. Resolving the room display name + peer nickname goes through the same pathDmRailSectionuses.Suggested UX (open to revision):
unhide_dm_thread(...)and the thread reappears in the rail.Scope notes
OUTBOUND_DMS_STORAGE_KEY(Phase 6 note inAGENTS.md).References
ui/src/components/app/chat_delegate.rs—hide_dm_thread,unhide_dm_thread,decide_hide_action.[AI-assisted - Claude]