Skip to content

Restore the feed bar container and name a single active filter - #575

Merged
bradleymackey merged 3 commits into
mainfrom
feed-filter-bar-surface
Sep 18, 2026
Merged

bradleymackey merged 3 commits into
mainfrom
feed-filter-bar-surface

Conversation

@bradleymackey

@bradleymackey bradleymackey commented Sep 16, 2026 •

Copy link
Copy Markdown
Member

Summary

The feed's bottom bar (tag pills + item count + Clear/Edit) lost its container and its descriptive filter text. These were two separate changes, months apart — and neither was #551, which only touched the backup screens:

  • The container — 510bfeaa (Rebuild the feed bottom bar on standard SwiftUI controls #555) deleted .background(Color.primary.opacity(0.05)) + .clipShape(RoundedRectangle(cornerRadius: 12)) from the status row, on the stated grounds that "the controls sit on the content the way a system bottom bar does". The controls were left with no surface at all, so the LazyVGrid scrolled directly under the status label.
  • The detail — 315486f5 (Fix edit mode access during search #499, January) replaced the localized prose "2 tag filters" with a bare • 🏷 2 badge.

For the record: there is no sort control in the app and never has been (git log --all -i -S "sort" -- Sources/VaultiOS is empty). Order is fixed to .relativeOrder at the store layer and rearranged by drag in edit mode.

What changed

  • The container is back exactly where it was: Color.primary.opacity(0.05) + RoundedRectangle(cornerRadius: 12) around the status row only, inset by 12pt. The tag pills stay above it, sitting directly on the content — that is the original arrangement and it is the one restored here.
  • A single active filter is named (• 🏷 work). The pill row scrolls horizontally, so an active tag can sit off-screen; the name is information the bare count never carried. With more than one filter there is no room for names inside the container, so it shows the bare count exactly as before (• 🏷 2).
  • Item count is localized: statusLabel hardcoded English count == 1 ? "item" : "items". It now uses the existing feedViewModel.searching.title.%lld plural via a new VaultDataModel.itemsCountDescription, mirroring filteringByTagsDescription. (That plural lives in VaultFeed's bundle, so it cannot be read from VaultiOS's localized(), which resolves against the "Feed" table.)

The button styles introduced by #555 (.bordered / .borderedProminent capsules, Toggle-based tag pills) are left alone — those were an accessibility fix, not part of the regression.

A material was tried and rejected

The first attempt replaced the container with a full-width .bar material across the whole safeAreaInset. Two problems, both visible in the history of this branch:

  1. It pulled the tag pills inside the band, which is not how the bar is meant to read.
  2. .background(.bar) applied directly puts the label in a vibrancy context. That is not cosmetic — it rendered the key.horizontal and tag.fill icons completely invisible and forced the secondary text to black. (.background { Rectangle().fill(.bar) } avoids the vibrancy, if a material is ever wanted here.)

Manifesto

Reviewed against MANIFESTO.md. C5 / C2 are the live ones and drove an explicit non-goal: no "matched of total" count. The feed query filters on visibility == always and unions search-passphrase matches only when the query matches (PersistedLocalVaultStore.swift:42-49, 102-106), so items.count deliberately excludes passphrase-hidden items. Surfacing an unfiltered total would disclose that hidden items exist and let a coercer diff the two numbers. The status label still counts only what the feed already displays, and itemsCountDescription carries a doc comment saying so. Nothing else is touched: no bulk operation (C1), no telemetry (C3), no auth change (C4), no undo or audit surface (C6), no default flipped (C7), no step removed from a sensitive flow (C8), no duress feature advertised (C9), no payload change (C10).

Testing

  • Full iOSAllTests plan passes locally on iPhone 18 Pro Max / iOS 27.0.
  • Feed snapshots re-recorded and visually compared against git show 510bfeaa^:…/unifiedBar_multipleTagsFiltered.1.png to confirm the layout matches the original. Two added — unifiedBar_singleFilterIsNamed and unifiedBar_multipleFiltersFallBackToCount — pinning both sides of the naming threshold.
  • The navigation and autofill suites that embed the feed were re-recorded too.

⚠️ Automatic CI is still disabled (#548), so this is local verification only.

🤖 Generated with Claude Code

bradleymackey and others added 2 commits September 16, 2026 16:37
The feed bar lost two things in separate changes:

- #555 removed its container (Color.primary.opacity(0.05) +
  RoundedRectangle) on the grounds that the controls should sit on
  the content like a system bottom bar. But a system bar carries a
  material and this one got nothing, so the grid scrolled under the
  status label with no separation.
- #499 replaced the localized "N tag filters" prose with a bare
  numeric badge, orphaning filteringByTagsDescription and its
  string key.

Restore separation with a material rather than the old hand-rolled
fill, and name the active tag when exactly one is applied — the pill
row scrolls, so an active tag can sit off-screen. Two names already
truncate mid-word beside Clear/Edit, so past one this falls back to
the revived pluralized count.

The material is wrapped in a Rectangle rather than applied as
.background(.bar) directly: the latter puts the label in a vibrancy
context, which rendered the key and tag icons invisible and forced
the secondary text to black.

Item counts now use the existing feedViewModel.searching.title plural
instead of a hardcoded English "item"/"items".

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The material was applied to the whole safeAreaInset stack, which
pulled the tag pills inside the band. They belong above it, sitting
directly on the content, with only the status row carrying a surface.

Put the container back where it was — Color.primary.opacity(0.05) +
RoundedRectangle(12) around the status row alone — and drop the
material. Its horizontal inset leaves no room for "N tag filters", so
multiple filters show the bare count as they did originally; a single
filter still shows its name, which fits.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@bradleymackey bradleymackey changed the title Give the feed bottom bar a surface and name the active filter Restore the feed bar container and name a single active filter Sep 17, 2026
The bordered button style fills the capsule in both states and
signals selection only through tint, which reads as near-identical
either way. TagPillView fills a selected pill and leaves an
unselected one outlined, which is unambiguous.

Keep the Toggle and .toggleStyle(.button) so the pills retain the
button trait and selected state that #555 added; only the visual
style reverts, via .buttonStyle(.plain) letting TagPillView draw
its own capsule.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@bradleymackey
bradleymackey merged commit 3fac070 into main Sep 18, 2026
@bradleymackey
bradleymackey deleted the feed-filter-bar-surface branch September 18, 2026 04:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant