Feeds + profiles: glass search, floating chrome, Bookmarks tab, profile consolidation
Created by: jedmund
Summary
Five commits, reshaping the top chrome across Search / Tag / Profile feeds, collapsing two profile code paths into one, and landing several foundation pieces they all share.
What changed
-
Search: custom glass input bar — drops
.searchablefor aCapsule-clippedTextFieldwith.glassEffect, 60% width, centered. Suggestions render inline below. The big "Search for a tag" empty-state is gone in favor of a longer placeholder string. -
FloatingFeedChrome+FloatingChromeButton— shared component with leading/center/trailing slots and a scroll-reactive glass capsule behind the center that fades in as content scrolls under.fadeThresholdOffsetlets callers with a hero header (pushedProfileView) delay the reveal until the header is concealed.FloatingChromeButtonis the 51pt glass circle reused everywhere; it uses.contentShape(Circle())before.clipShape+.glassEffectso iOS 26's glass modifier doesn't break hit-testing. -
PixillateHostingControllerlifecycle flags —hidesTabBar,hidesNavigationBar,appliesTrafficLightInset. Each opt-in, each reversible on pop (not on being-covered-by-a-pushed-detail). The nav-bar one hides the UIKit nav imperatively because SwiftUI's.toolbar(.hidden, for: .navigationBar)doesn't bridge throughUIHostingControlleronto our customUINavigationController.NavigationControllerHostthreads the flags throughhosted(_:)and exposes root-level variants for tab roots that need custom chrome. -
TagFeedView adopts the floating chrome: back chevron leading, scroll-reactive title pill center, filter menu trailing whose icon tracks the current sort (
flame/clock) and whose menu holds the sort picker + a "Time range…" entry. -
Profile tab → "Bookmarks". Tab root (
isCurrentUser: true): bookmarks grid only, no header, no Works picker. On iPad the visibility menu + settings gear render in the top tab-bar row via an overlay onTabView; on iPhone / compact width they fall back to an inline top-trailing cluster insideProfileView. Shared state via a smallBookmarksChromeState@Observable. PushedProfileView(isCurrentUser: false, from detail user-row or tag-pill): full profile header + works grid, back chevron leading, and a center title pill (avatar + name) that reveals with a bouncy spring once scroll crosses ~185pt (past the header) and collapses with a quick ease-out.ProfileHeaderViewgains a sharedpaletteColor(forUserID:)helper used for the avatar fallback circle. -
Profile consolidation:
IllustProfileViewControllerretired.IllustDetailViewController.handleProfileTapped()now delegates tonav.pushProfile?(userID:)— the same path every other profile push uses. The UIKit profile VC file (~350 lines) is deleted along with its local palette helper. One profile rendering path across the app; tag-pill taps, detail user-row taps, and the Bookmarks tab root all land in the sameProfileView.
Notable debugging notes
- iOS 26.2's
.glassEffect(.regular.tint(.clear))applied to aButton's label before.clipShape(Circle())can emit a hit region that doesn't line up with the visible circle — taps land on a surrounding transparent area of the effect and get absorbed by whatever sibling view occupies the adjacent space. The fix is to declare.contentShape(Circle())before.clipShape(Circle()) + .glassEffect(…). This got buried in chunk 3's debugging session on the pushed-profile back chevron. -
additionalSafeAreaInsets.topasserted on aUIHostingControllerthat still has a visible UIKit nav bar interacts badly with nav-bar.navigationTitle/.toolbarrendering (items go blank).appliesTrafficLightInsetis therefore only meant to pair withhidesNavigationBar.
Test plan
-
Search tab: new glass input bar reads, typeahead works, submit pushes the tag feed. -
Tap a tag pill from a detail → TagFeedView opens with the floating glass chrome. Back chevron pops. Filter icon reflects current sort (flame/clock); changing sort reloads the feed; "Time range…" opens the settings sheet. -
Bookmarks tab: no Works picker, no profile header. On iPad the visibility menu + settings gear sit in the top tab-bar row on the trailing edge. On iPhone / split-compact they render inline at the top of the view instead. Visibility change reloads the feed with the correct restrict; tapping the gear opens the sheet. -
From a detail, tap the user row → pushed ProfileViewopens with the full header. Scroll up ~200pt → the center avatar-and-name pill bounces in; scroll back to the top → the pill quickly fades out (no bounce). Back chevron pops cleanly. -
From a tag pill, tap a user row from the pushed profile's cell → that user's profile pushes on top. Nested nav works. -
iPad rotation + split-view resize: TabView-row overlay appears/disappears as the horizontal size class changes between regular and compact; buttons don't duplicate. -
iPadOS 26 windowed mode: floating chrome clears the traffic-light cluster. -
Hero transitions from feed cells still work end-to-end; second-dismiss cell-rect fix from #20 isn't regressed.