Skip to content

feat: surface recently viewed items in the vault list #59

@allisson

Description

@allisson

Summary

The tracking infrastructure for recently viewed items already exists but is never shown to the user. This issue is about surfacing that data in the main vault list as a "Recently Viewed" section at the top, reducing the number of taps/clicks needed to reach frequently accessed items — which is the primary friction point in a copy-paste workflow without browser autofill.

What already exists

  • apps/web/src/lib/recentlyViewed.tspushRecentlyViewed(vaultId, itemId), getRecentlyViewed(vaultId): string[], clearRecentlyViewed(vaultId). Stores up to 10 item IDs per vault in localStorage under the key bp:recent:<vaultId>.
  • apps/web/src/hooks/useRecentlyViewed.ts — calls pushRecentlyViewed when an item is viewed; already called from $itemId.tsx.

getRecentlyViewed is never called by any UI component — only by tests.

Desired behaviour

  1. When the vault list is open and has items, show a "Recently Viewed" section above the main item list (or as a pinned top section within the list).
  2. The section shows the last N items (suggested: 5) the user opened in the current vault, in reverse-chronological order (most recent first).
  3. Items that no longer exist (deleted, moved) are silently excluded.
  4. If there are no recently viewed items yet (fresh vault, cleared state), the section is hidden — no empty state needed.
  5. Tapping an item in the section navigates to the item detail, same as the regular list.
  6. The section should be visually distinct but not dominant — a small label ("Recently Viewed") and standard ItemCard rows is sufficient.

Scope notes

  • Cross-vault "All Vaults" view: recently viewed could optionally show items across all vaults. Keep this out of scope for the initial implementation; per-vault is sufficient.
  • "All Vaults" aggregate view: the existing bp:recent:<vaultId> key is per-vault. The aggregate view may show a merged list in a follow-up.
  • Persistence: localStorage is intentional (survives page reloads, cleared on session.clear()/logout via existing clear calls). No server changes required.
  • IndexedDB vs localStorage: the current implementation uses localStorage. This is acceptable; no migration needed unless a future change requires it.

Implementation notes

  • Read recently viewed IDs via getRecentlyViewed(activeVaultId) in the vault list component.
  • Resolve IDs against the already-loaded items array from useVaultItems() — no extra network call.
  • Filter out IDs that are no longer in the items array (deleted items).
  • Limit display to 5 items maximum.
  • The clearRecentlyViewed call on session.lock() / session.clear() already exists; verify it covers the "lock screen" path so recent history does not persist after lock.

Acceptance criteria

  • "Recently Viewed" section appears at the top of the vault item list when at least one item has been previously opened.
  • Section shows up to 5 items, most recent first.
  • Items deleted from the vault are silently excluded from the section.
  • Section is hidden when there are no recently viewed items.
  • Tapping an item navigates to its detail view.
  • Recently viewed list is cleared on logout and account lock.
  • No regression on the "All Vaults" aggregate view.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions