refactor(phase0): devices, manual apps, focus editor and changelog become shells - #197
Closed
adamXbot wants to merge 1 commit into
Closed
refactor(phase0): devices, manual apps, focus editor and changelog become shells#197adamXbot wants to merge 1 commit into
adamXbot wants to merge 1 commit into
Conversation
…come shells
Batch 2 of the Rust-core Phase 0 page conversions (11/35 done). These
four pages shared two server concerns, and both now come from the API:
- Flag gates. `resolveFlagFromDb(key) !== "on" → notFound()` becomes
RequireFlagGate, which reads the resolved value from
GET /api/feature-flags (the `flags[].currentValue` field — the same
resolver output). It fails CLOSED: a failed flag read renders the 404
rather than the page, because these gates hide surfaces that
shouldn't exist for the install. notFound() is thrown during render
(not from the effect), which is the only way it works client-side.
- Initial data. Every one of these lists already had an exact API twin,
so no new endpoints were needed:
devices → /api/devices (already includes appCount, and
DevicesView already fetched it in refresh())
manual apps → /api/manual-apps (already returned the exact
{ apps, sources } pair the page assembled by hand)
focus editor → /api/focus (already returned all six fields; the
new FocusEditLoader maps them and holds the form
back until they land, since the form stages edits
from its initial props)
changelog → /api/apps for the filter dropdown
The `initialX` props stay optional on DevicesView / ManualAppsView /
UniversalChangelogView so their Storybook stories keep seeding
fixtures; in Storybook the mount fetch simply fails and the seeded
state stands.
Verified by hand where no e2e covers it: both gated pages render with
their data through the gate, the manual-app form still defaults to
Safari web app (sources[0], unchanged — both create paths reset from
live sources), and flipping flag.page.manual_apps off renders the real
404 page.
Gate: tsc clean; biome clean; unit 462 pass / 0 fail; full Playwright
46 passed; visual net 13/13 pixel-identical against pre-conversion
baselines captured via stash.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This was referenced Aug 16, 2026
Collaborator
Author
|
Superseded by #199, which consolidates Phase 0 batches 2–6 into a single PR (32 of 35 pages). This branch's commits are all contained in that one — closing to avoid a partial merge landing ahead of it. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Batch 2 of Rust-core Phase 0 — 11 of 35 pages converted. These four shared exactly two server concerns, and both now resolve through the API.
Flag gates.
resolveFlagFromDb(key) !== "on" → notFound()becomesRequireFlagGate, reading the resolved value fromGET /api/feature-flags(flags[].currentValue— the same resolver output, not a re-implementation). Two details worth review attention:notFound()is thrown during render from state, not from the effect — the only way it works in a client component.Initial data. Every list already had an exact API twin, so no new endpoints were needed:
/api/devicesappCount;DevicesViewalready fetched it inrefresh()/api/manual-apps{ apps, sources }pair the page assembled by hand/api/focusFocusEditLoadermaps them/api/appsFocusEditLoaderholds the form back until the fetch lands —FocusEditFormstages edits from its initial props, so mounting with placeholders then swapping would either be ignored or clobber user input.The
initialXprops stay optional on the three views so their Storybook stories keep seeding fixtures (there the mount fetch fails and the seeded state stands).Verification
sources[0]— unchanged, since both create paths reset from livesourcesand the deep-link path's fallback was alreadyweb_clip); flippingflag.page.manual_appsoff renders the real 404 page.tscclean · biome clean · unit 462 pass / 0 fail · full Playwright 46 passed · visual net 13/13 pixel-identical against pre-conversion baselines captured via stash.Next
Batch 3 takes the pages needing small new read endpoints (stats, privacy map, shortlist, review-recommendations), then the big three (dashboard, app detail, apps grid), with the layout/CSP batch last and coming to you for sign-off.
🤖 Generated with Claude Code