feat(ui): unread-only sidebar filter (LC-308) #331

Merged
longjacksonle merged 1 commit from feat/lc-308-unread-only-sidebar into main 2026-06-15 03:12:51 +02:00

Summary

A device-local "Unread only" toggle at the top of the sidebar that hides rooms/DMs (and now-empty sections) with no unread and no mention, keeping the currently-open room visible.

Approach

Pure CSS, no JS scan. Rows/sections are marked data-lc-sb-row / data-lc-sb-section; the unread/mention badge spans carry data-lc-unread / data-lc-mention; two rules gated on html[data-lc-unread-only] hide anything not :has()-ing a non-empty badge or an [aria-current="page"]. The unread badge OOB-swaps live on every new message / read, so :has()/:empty re-evaluate automatically: a new message re-reveals a row, reading it re-hides it.

Changes

  • base.html: applyUnreadOnly + __lcToggleUnreadOnly (no-flash, localStorage, mirrors __lcToggleSidebar).
  • partials/sidebar.html: the toggle button (aria-pressed).
  • layout.html: IIFE syncing the button's aria-pressed from the <html> flag on htmx settle (covers sidebar re-renders).
  • partials/sidebar_nav.html: data-lc-sb-row / data-lc-sb-section markers + collapsed-category total badges marked.
  • partials/unread_badge.html + ws/unread_badge.html: data-lc-unread. partials/mention_badge.html: data-lc-mention.
  • server/assets/main.css: the two :has() filter rules.
  • locales/{en,es}/partials.ftl: sidebar-unread-only.

Muted rooms already suppress their unread badge, so they stay out of the unread view; the open room stays visible via the aria-current exemption.

Testing

  • just check, just fmt, just test, just test-saas all pass.
  • Updated three muted-room/DM assertions that pinned the exact empty-badge markup to include the new data-* attribute. No new test binary (pure client-side over existing markup; mirrors theme-toggle / jump-unread). No LC-77 fixture change (message.html untouched).

Not operator-visible (no env/config/contract change).

## Summary A device-local "Unread only" toggle at the top of the sidebar that hides rooms/DMs (and now-empty sections) with no unread and no mention, keeping the currently-open room visible. ## Approach Pure CSS, no JS scan. Rows/sections are marked `data-lc-sb-row` / `data-lc-sb-section`; the unread/mention badge spans carry `data-lc-unread` / `data-lc-mention`; two rules gated on `html[data-lc-unread-only]` hide anything not `:has()`-ing a non-empty badge or an `[aria-current="page"]`. The unread badge OOB-swaps live on every new message / read, so `:has()`/`:empty` re-evaluate automatically: a new message re-reveals a row, reading it re-hides it. ## Changes - `base.html`: `applyUnreadOnly` + `__lcToggleUnreadOnly` (no-flash, localStorage, mirrors `__lcToggleSidebar`). - `partials/sidebar.html`: the toggle button (`aria-pressed`). - `layout.html`: IIFE syncing the button's `aria-pressed` from the `<html>` flag on htmx settle (covers sidebar re-renders). - `partials/sidebar_nav.html`: `data-lc-sb-row` / `data-lc-sb-section` markers + collapsed-category total badges marked. - `partials/unread_badge.html` + `ws/unread_badge.html`: `data-lc-unread`. `partials/mention_badge.html`: `data-lc-mention`. - `server/assets/main.css`: the two `:has()` filter rules. - `locales/{en,es}/partials.ftl`: `sidebar-unread-only`. Muted rooms already suppress their unread badge, so they stay out of the unread view; the open room stays visible via the `aria-current` exemption. ## Testing - `just check`, `just fmt`, `just test`, `just test-saas` all pass. - Updated three muted-room/DM assertions that pinned the exact empty-badge markup to include the new `data-*` attribute. No new test binary (pure client-side over existing markup; mirrors theme-toggle / jump-unread). No LC-77 fixture change (message.html untouched). Not operator-visible (no env/config/contract change).
feat(ui): unread-only sidebar filter (LC-308)
All checks were successful
check-secrets / Nosey parker (push) Successful in 3s
check-secrets / Kingfisher (push) Successful in 5s
check-secrets / TruffleHog (push) Successful in 5s
check-secrets / TruffleHog (pull_request) Successful in 5s
check-secrets / Nosey parker (pull_request) Successful in 6s
check-secrets / Kingfisher (pull_request) Successful in 7s
Check / clippy + fmt + tests (pull_request) Successful in 2m14s
Create release / Create release from merged PR (pull_request) Has been skipped
32f26a43d7
Add a device-local "Unread only" toggle at the top of the sidebar that hides every room/DM row (and now-empty section) with no unread and no mention, keeping the currently-open room visible.

The hiding is pure CSS: rows and sections are marked data-lc-sb-row / data-lc-sb-section, the unread/mention badge spans carry data-lc-unread / data-lc-mention, and two rules gated on html[data-lc-unread-only] hide anything that does not :has() a non-empty badge or an aria-current="page". Because the unread badge OOB-swaps live on every new message / read, :has() and :empty re-evaluate automatically, so a new message re-reveals a row and reading it re-hides it with no JS scan and nothing to tear down.

The toggle persists in localStorage and is applied no-flash by the base.html bootstrap (mirrors __lcToggleSidebar); a small layout.html IIFE keeps the button's aria-pressed in step after a sidebar re-render. Muted rooms already suppress their unread badge, so they stay out of the unread view with no special-casing; collapsed-category total badges are marked too so a collapsed category with unread is not hidden.

Test-contract: three muted-room/DM assertions that pinned the exact empty-badge markup now include the new data-* attribute. (The two LC-304 keyword test files also picked up trivial cargo fmt canonicalization.)

#LC-308
#LC-309

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
longjacksonle deleted branch feat/lc-308-unread-only-sidebar 2026-06-15 03:12:51 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/lets-chat!331
No description provided.