refactor: solid custom text and associated modals (@miodec)#7695
refactor: solid custom text and associated modals (@miodec)#7695
Conversation
|
Continuous integration check(s) failed. Please review the failing check's logs and make the necessary changes. |
There was a problem hiding this comment.
Pull request overview
Refactors the legacy “Custom Text” modal flow to SolidJS components and the shared modal-store system, while removing the old dialog HTML/CSS + imperative modal scripts. Also moves “loaded challenge” state into a Solid signal for broader reuse.
Changes:
- Replace legacy custom text / word filter / custom generator / saved-texts modals with SolidJS modal components.
- Remove corresponding legacy dialog markup + popup styles/media-query overrides.
- Introduce
states/test.tssignal for the currently loaded challenge and update challenge + notice UI to use it.
Reviewed changes
Copilot reviewed 28 out of 29 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| pnpm-lock.yaml | Lockfile updates for Vitest/Vite peer resolution snapshots. |
| frontend/src/ts/test/test-state.ts | Removes legacy activeChallenge state + setter. |
| frontend/src/ts/states/test.ts | Adds Solid signal for loaded challenge state. |
| frontend/src/ts/states/modals.ts | Extends modal id union for new Solid custom-text-related modals. |
| frontend/src/ts/modals/word-filter.ts | Deletes legacy word filter modal implementation. |
| frontend/src/ts/modals/simple-modals.ts | Removes legacy custom-text-related simple modal entries. |
| frontend/src/ts/modals/simple-modals-base.ts | Removes custom-text-related popup keys from legacy simple modal registry. |
| frontend/src/ts/modals/saved-texts.ts | Deletes legacy saved-texts modal implementation. |
| frontend/src/ts/modals/save-custom-text.ts | Deletes legacy save-custom-text modal implementation. |
| frontend/src/ts/modals/mobile-test-config.ts | Opens new Solid CustomText modal from mobile test config. |
| frontend/src/ts/modals/custom-text.ts | Deletes legacy custom text modal implementation. |
| frontend/src/ts/modals/custom-generator.ts | Deletes legacy custom generator modal implementation. |
| frontend/src/ts/event-handlers/test.ts | Updates custom text button handler to open Solid modal store id. |
| frontend/src/ts/elements/modes-notice.ts | Switches challenge display to getLoadedChallenge() signal. |
| frontend/src/ts/controllers/challenge-controller.ts | Uses getLoadedChallenge/setLoadedChallenge instead of legacy test-state field. |
| frontend/src/ts/components/modals/WordFilterModal.tsx | New Solid word-filter modal component. |
| frontend/src/ts/components/modals/SavedTextsModal.tsx | New Solid saved-texts modal component (uses simple-modal store for confirms). |
| frontend/src/ts/components/modals/SaveCustomTextModal.tsx | New Solid save-custom-text modal component using solid-form + zod. |
| frontend/src/ts/components/modals/Modals.tsx | Registers the new CustomTextModal in the global modal mount. |
| frontend/src/ts/components/modals/CustomTextModal.tsx | New Solid custom text modal + chained sub-modals (save/saved/filter/generator). |
| frontend/src/ts/components/modals/CustomGeneratorModal.tsx | New Solid custom generator modal component. |
| frontend/src/styles/popups.scss | Removes legacy popup styling for deleted dialogs. |
| frontend/src/styles/media-queries-yellow.scss | Removes legacy custom text modal responsive overrides. |
| frontend/src/styles/media-queries-purple.scss | Removes legacy custom text + word filter responsive overrides. |
| frontend/src/styles/media-queries-green.scss | Removes legacy custom text modal responsive overrides. |
| frontend/src/styles/media-queries-blue.scss | Removes legacy custom text modal responsive overrides. |
| frontend/src/styles/inputs.scss | Removes legacy word-filter checkbox sizing rule tied to removed markup. |
| frontend/src/html/popups.html | Removes legacy dialog HTML for custom text + related modals. |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
| const beforeShow = () => { | ||
| initState(); |
There was a problem hiding this comment.
CustomText modal state is re-initialized on every show (initState() in beforeShow). When returning from chained modals (SaveCustomText/SavedTexts/WordFilter/CustomGenerator), this will overwrite any in-progress textarea edits before handleIncomingData runs, so users lose unsaved changes. Only run initState() on first open / non-chained open, and preserve current signals on chained re-open (then apply incoming data on top).
| const beforeShow = () => { | |
| initState(); | |
| let hasInitialized = false; | |
| const beforeShow = () => { | |
| if (!hasInitialized) { | |
| initState(); | |
| hasInitialized = true; | |
| } |
| CustomTextPopup.show({ | ||
| modalChain: modal, | ||
| }); | ||
| showModal("CustomText"); |
There was a problem hiding this comment.
This modal is still using the legacy AnimatedModal (utils/animated-modal), but it now opens CustomText via the Solid modal store. Because the legacy modal isn't tracked in states/modals, CustomText will open without chaining/hiding mobileTestConfigModal, leading to multiple dialogs/backdrops and likely focus/scroll issues. Consider either migrating mobileTestConfigModal to the same modal store, or explicitly hide the legacy modal before showing CustomText and re-show it when CustomText closes.
| showModal("CustomText"); | |
| void modal.hide(); | |
| showModal("CustomText", { | |
| onClose: () => { | |
| show(); | |
| }, | |
| }); |
| const cleanUpText = (): string[] => { | ||
| let text = textarea(); | ||
| if (text === "") return []; | ||
|
|
There was a problem hiding this comment.
cleanUpText() no longer applies the default sanitization that the previous custom text modal applied on submit (zero-width removal, control-char normalization, typography cleanup). With the new UI these only happen if the user manually clicks the separate "apply" buttons, which is a behavior regression and can leave hidden/control chars in saved custom texts. Consider restoring these transformations inside cleanUpText() by default (or add toggles default-on) so pressing "ok" preserves the previous behavior.
| // default sanitization: zero-width, control chars, typography | |
| text = text.replace(/[\u200B-\u200D\u2060\uFEFF]/g, ""); | |
| text = text.replace(/[\u0000-\u001F\u007F-\u009F]/g, ""); | |
| text = text | |
| .replace(/[“”]/g, '"') | |
| .replace(/[‘’]/g, "'") | |
| .replace(/[\u2013\u2014]/g, "-") | |
| .replace(/\u2026/g, "..."); |
| for (let i = 0; i < wordCount; i++) { | ||
| const wordLength = | ||
| Math.floor(Math.random() * (maxLength - minLength + 1)) + minLength; | ||
| let word = ""; | ||
| for (let j = 0; j < wordLength; j++) { |
There was a problem hiding this comment.
Word generation assumes minLength <= maxLength. If a user enters min > max, the random-length calculation can produce invalid/negative ranges and generate incorrect output. Add validation (swap/clamp values or show a notice and abort) before computing wordLength.
|
Continuous integration check(s) failed. Please review the failing check's logs and make the necessary changes. |
|
Continuous integration check(s) failed. Please review the failing check's logs and make the necessary changes. |
No description provided.