Restore launch UI state loading#119
Open
jamesfredley wants to merge 5 commits into
Open
Conversation
There was a problem hiding this comment.
Pull request overview
Restores the Launch UI’s initial state/loading behavior after the dependency/Zustand migration regressions (PR #111), ensuring options/features populate correctly and dev/local tooling works consistently.
Changes:
- Add request-staleness guards to async options/features loads and reload features when query-driving fields change.
- Restore synchronous-ish store initialization for the Launch UI and tighten “Start Over” reset behavior.
- Fix local dev start/proxy/version-feed behavior and restore/enhance lint + test coverage expectations.
Reviewed changes
Copilot reviewed 9 out of 10 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| dev-proxy-server/src/commands.js | Updates the version server route for the Express 5 routing/wildcard behavior. |
| app/launch/src/state/store.js | Adds request IDs/keys to prevent stale async updates; updates reset and features-loading behavior. |
| app/launch/src/state/ApplicationState.jsx | Changes how initial state is applied to the Zustand store at app start. |
| app/launch/src/components/FeatureSelector/FeatureSelector.jsx | Avoids recreating the keydown handler each render by memoizing correctly. |
| app/launch/src/components/ErrorView/ErrorView.jsx | Import ordering cleanup for MUI components. |
| app/launch/src/components/Application/tests/useAvailableFeatures.test.jsx | Extends SDK mock + asserts the rendered features list content. |
| app/launch/src/components/Application/tests/Application.test.jsx | Strengthens assertions to verify key UI elements render. |
| app/launch/package.json | Switches to ESM package mode and improves start:local portability + lint script quoting; adds deps for cross-env and hooks linting. |
| app/launch/package-lock.json | Lockfile updates for added dependencies. |
| app/launch/eslint.config.js | Enables react-hooks lint rules and adjusts no-unused-vars options for catch handling. |
Files not reviewed (1)
- app/launch/package-lock.json: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Initialize the Zustand store before paint, reload feature data when feature-query fields change, ignore stale async loader responses, and keep reset defaults safe when option buckets are missing. Assisted-by: opencode:gpt-5.5
Assert that the launch form and action row render instead of only checking for a defined test container. Assisted-by: opencode:gpt-5.5
Reorder MUI imports and memoize the feature modal keyboard handler factory result so lint can run cleanly. Assisted-by: opencode:gpt-5.5
Make local launch scripts cross-platform, wire the React hooks lint plugin, and update the Express 5 version feed route used by local browser QA. Assisted-by: opencode:gpt-5.5
Keep the launch form children hidden until the Zustand store has been initialized, restore production-style standard selects, and preserve the form row spacing used by start.grails.org. Assisted-by: opencode:gpt-5.5
6b45829 to
c3e90c9
Compare
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.
Summary
This PR restores the launch UI behavior and production-style formatting after the PR #111 Recoil-to-Zustand migration.
It now covers the state loading fixes, async race guards, reset fallback, visual parity fixes, local proxy tooling, lint setup, and tests needed to verify the migrated launch page.
User-visible fixes
Default Included Features (8)renders again instead of falling back to an empty list.Featuresmodal receives current feature data instead of stale or empty state.Start Overno longer crashes when theselect-optionsresponse omits optional option buckets.start.grails.orgpage.Root causes and fixes
1. Zustand initialization happened too late
PR #111 moved launch state from Recoil to Zustand. The migrated launch tree could render children before the Zustand store had been seeded with initial data, saved form data, deep-link values, and default values.
Fix
ApplicationStatenow initializes the store fromuseLayoutEffectand only renders children after initialization completes.Why this works
The child tree no longer reads empty first-render values for fields such as
appType,javaVersion,gorm,servlet, andreloading. It also avoids render-phase store mutation and removes the MUI controlled/uncontrolled warnings seen during browser QA.2. Feature loads missed important query fields
The feature endpoints depend on more than the selected version and app type. The request URL also changes when these fields change:
javaVersionservletgormreloadingFix
useLoadFeaturesEffectnow watches the full set of feature-query fields and reloads both available features and default included features when they change.Why this works
After option defaults settle, the app requests features with the same query inputs production uses. This restores populated default features and feature modal data.
3. Stale async responses could overwrite current state
Option and feature loading are async. An older request could resolve after the visible form state changed and still write stale data into the shared Zustand store.
Fix
The option, feature, and default-feature loaders now use request ids plus request keys derived from the inputs that affect each request.
Why this works
A response updates state only if it still belongs to the latest request and still matches the current visible query inputs. Older successes and failures are ignored.
4. Reset assumed every option bucket was present
resetFormused option defaults directly. Copilot correctly pointed out that optional buckets such as servlet, GORM, reloading, or JDK version may be missing.Fix
resetFormnow loads options when needed, uses optional chaining for option buckets, and falls back to current store values when a bucket is absent.Why this works
The reset path no longer crashes when an optional bucket is missing and still applies safe defaults for the fields that are available.
5. Select and form spacing regressed from production
The migrated MUI select controls were rendering with outlined-select sizing instead of the production standard underline layout. That made the form row and dropdown spacing differ from the current
start.grails.orgpage.Fix
This PR restores standard MUI selects, fixes select label/id wiring, keeps select values controlled with an empty-string fallback, and restores the form row spacing used by production.
Why this works
Browser QA confirmed the local form geometry matches the production page: select wrappers are 306 x 48, standard underline inputs are 306 x 32, and the Project Type menu opens at 306px wide with 36px options.
6. Local tooling and lint gates were unreliable
The local start command used POSIX-style environment variable syntax, the local feed URL was not explicit, the Express 5 wildcard route needed Express 5 syntax, and the migrated React code needed hooks linting to run consistently.
Fix
This PR:
cross-env.start:localto useVITE_VERSION_FEED=http://localhost:8088/grails-version-feed.json./{*splat}.eslint-plugin-react-hooks.react-hooks/rules-of-hooksas an error.react-hooks/exhaustive-depsas a warning.type: modulefor the ESM ESLint config.Why this works
The local proxy flow is portable across Windows and POSIX shells, and lint now validates the migrated React hooks code instead of failing on tooling configuration.
Review feedback addressed
resetFormmissing option buckets.ApplicationStaterender-phase side effects.Assisted-by: opencode:gpt-5.5.What changed
start.grails.org.Verification
Commands run successfully:
Browser QA was run through the local proxy path with
npm run start:local:Default Included Features (8)rendered.Featuresmodal opened with populated feature groups.postgresfiltered the modal toPostgresSQL.PostgresSQLupdatedAdditional Selected Features (1).start.grails.orglayout.Scope notes
start:localchanges are dev-tooling fixes only.react-hooks/exhaustive-depsis enabled as a warning so this PR restores lint visibility without expanding into a broad hooks cleanup.Assisted-bytrailer.