Skip to content

Split modals into separate component#62

Merged
imnasnainaec merged 2 commits into
create-interlinearizer-projectfrom
split-off-modals
May 13, 2026
Merged

Split modals into separate component#62
imnasnainaec merged 2 commits into
create-interlinearizer-projectfrom
split-off-modals

Conversation

@imnasnainaec

@imnasnainaec imnasnainaec commented May 12, 2026

Copy link
Copy Markdown
Contributor

Suggested refactor for #33

This is what it might look like to have the models handled in their own component. The only change in behavior is that after a project is created via the select-project modal, you don't return to that modal, which I think would be simpler for users anyway.

Note: the tests haven't been updated to match this change.
Also: some of the cross-component types should perhaps be extracted to a common types file.
Devin has a few useful flags as well if this goes through: https://app.devin.ai/review/sillsdev/interlinearizer-extension/pull/62


This change is Reviewable

Summary by CodeRabbit

  • Refactor
    • Improved internal organization of project modal management for better code maintainability and clearer separation of concerns.

Review Change Stack

@imnasnainaec imnasnainaec added the 🟪Idea Idea-priority PR: can be closed... label May 12, 2026
@coderabbitai

coderabbitai Bot commented May 12, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2819d22e-6c05-437b-b1aa-0a1256bccead

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The PR refactors project modal state management by extracting modal UI orchestration from InterlinearizerLoader into a new ProjectModals component. Type contracts for modal state and persisted active project are defined in ProjectModals and SelectInterlinearProjectModal. The loader is simplified to delegate all modal logic to ProjectModals.

Changes

Modal State Extraction and Delegation

Layer / File(s) Summary
Modal State Type Contracts
src/components/SelectInterlinearProjectModal.tsx, src/components/ProjectModals.tsx
ActiveProjectState type captures the persisted subset of project fields from InterlinearProjectSummary. ModalState union type ('none' | 'select' | 'create' | 'metadata') defines visible modal states.
ProjectModals Component Implementation
src/components/ProjectModals.tsx
ProjectModals orchestrates modal visibility, persists activeProject in WebView state, maintains local UI state for metadata project and origin tracking, syncs metadataProject from activeProject when entering metadata mode, and provides memoized callbacks for opening metadata, saving changes (merging into activeProject when applicable), and deleting projects. Conditionally renders SelectInterlinearProjectModal, CreateProjectModal, and ProjectMetadataModal based on modal state.
InterlinearizerLoader Refactoring
src/components/InterlinearizerLoader.tsx
Imports ProjectModals and ModalState, converts activeProject handling from getter/setter to read-only snapshot, simplifies command handler to toggle modal state to create or metadata (gating metadata on activeProject existence), and removes inline modal rendering, callbacks (handleViewInfo, project-created/saved/deleted flows, metadata source tracking), and state setters (setMetadataProject, resetActiveProject). Renders single ProjectModals component with modal, projectId, setModal, and useWebViewState.

Sequence Diagram(s)

sequenceDiagram
  participant SelectModal as SelectInterlinearProjectModal
  participant ProjectModals
  participant MetadataModal as ProjectMetadataModal
  participant WebViewState as WebView State

  SelectModal->>ProjectModals: User opens metadata for project
  ProjectModals->>ProjectModals: Set metadataProject from selection
  ProjectModals->>MetadataModal: Render with metadataProject
  MetadataModal->>ProjectModals: Save metadata changes
  ProjectModals->>ProjectModals: Merge updates into activeProject
  ProjectModals->>WebViewState: Persist updated activeProject
  ProjectModals->>SelectModal: Return to originating modal
  
  SelectModal->>ProjectModals: User selects project
  ProjectModals->>WebViewState: Persist as activeProject
  ProjectModals->>ProjectModals: Set modal to none
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 Modal state hops from loader to home,
ProjectModals now has the throne,
Callbacks and handlers find their way,
Simpler paths to save the day,
State refactored, clean and neat! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: extracting modal UI/state logic from InterlinearizerLoader into a new ProjectModals component.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch split-off-modals

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/components/ProjectModals.tsx (1)

14-24: ⚡ Quick win

Add @returns to the JSDoc block for consistency.

The coding guidelines require @returns for all functions except those returning void or Promise<void>. React components return JSX.Element, so should include this tag. The similar component SelectInterlinearProjectModal in SelectInterlinearProjectModal.tsx (line 59) includes @returns, so adding it here maintains consistency across the codebase.

📝 Suggested JSDoc addition
  * `@param` props.useWebViewState - Hook for reading and writing values persisted in the WebView's
  *   saved state (survives tab restores)
+ * `@returns` A container that conditionally renders one of the project modals (select, create, or
+ *   metadata) based on the current modal state, or an empty div when no modal is open.
  */
 export default function ProjectModals({

As per coding guidelines: "Every function and method—exported or internal—must have a JSDoc block with a summary sentence describing what the function does and why it exists, @param for every parameter, @returns describing the return value (omit only for void/Promise<void>)".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/ProjectModals.tsx` around lines 14 - 24, The JSDoc for the
ProjectModals React component is missing an `@returns` tag; update the comment
block above the ProjectModals component to include "@returns {JSX.Element} The
rendered project modals" (or similar) so it matches the project's guideline and
the other component examples like SelectInterlinearProjectModal; ensure the tag
is added to the existing JSDoc that documents props.modal, props.projectId,
props.setModal, and props.useWebViewState for the ProjectModals component.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/components/ProjectModals.tsx`:
- Around line 14-24: The JSDoc for the ProjectModals React component is missing
an `@returns` tag; update the comment block above the ProjectModals component to
include "@returns {JSX.Element} The rendered project modals" (or similar) so it
matches the project's guideline and the other component examples like
SelectInterlinearProjectModal; ensure the tag is added to the existing JSDoc
that documents props.modal, props.projectId, props.setModal, and
props.useWebViewState for the ProjectModals component.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d3737e7d-8a13-4842-bc80-913b3703225f

📥 Commits

Reviewing files that changed from the base of the PR and between a92dfed and ba42e1b.

📒 Files selected for processing (3)
  • src/components/InterlinearizerLoader.tsx
  • src/components/ProjectModals.tsx
  • src/components/SelectInterlinearProjectModal.tsx

@alex-rawlings-yyc

Copy link
Copy Markdown
Contributor

Somehow I didn't quite catch what you meant when you made the earlier comment. This looks great; thanks for doing it. I will make some adjustments and get it tested properly.

@alex-rawlings-yyc alex-rawlings-yyc left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alex-rawlings-yyc reviewed 4 files and all commit messages.
Reviewable status: :shipit: complete! all files reviewed, all discussions resolved (waiting on imnasnainaec).

@imnasnainaec
imnasnainaec merged commit c305324 into create-interlinearizer-project May 13, 2026
2 checks passed
@imnasnainaec
imnasnainaec deleted the split-off-modals branch May 13, 2026 12:26
alex-rawlings-yyc added a commit that referenced this pull request May 13, 2026
Co-authored-by: alex-rawlings-yyc <alex.rawlings@wycliffe.ca>
alex-rawlings-yyc added a commit that referenced this pull request May 13, 2026
Co-authored-by: alex-rawlings-yyc <alex.rawlings@wycliffe.ca>
alex-rawlings-yyc added a commit that referenced this pull request May 13, 2026
Co-authored-by: alex-rawlings-yyc <alex.rawlings@wycliffe.ca>
alex-rawlings-yyc added a commit that referenced this pull request May 13, 2026
Co-authored-by: alex-rawlings-yyc <alex.rawlings@wycliffe.ca>
alex-rawlings-yyc added a commit that referenced this pull request May 14, 2026
Co-authored-by: alex-rawlings-yyc <alex.rawlings@wycliffe.ca>
alex-rawlings-yyc added a commit that referenced this pull request May 14, 2026
Co-authored-by: alex-rawlings-yyc <alex.rawlings@wycliffe.ca>
alex-rawlings-yyc added a commit that referenced this pull request May 15, 2026
Co-authored-by: alex-rawlings-yyc <alex.rawlings@wycliffe.ca>
alex-rawlings-yyc added a commit that referenced this pull request May 19, 2026
Co-authored-by: alex-rawlings-yyc <alex.rawlings@wycliffe.ca>
alex-rawlings-yyc added a commit that referenced this pull request May 20, 2026
Co-authored-by: alex-rawlings-yyc <alex.rawlings@wycliffe.ca>
alex-rawlings-yyc added a commit that referenced this pull request May 21, 2026
Co-authored-by: alex-rawlings-yyc <alex.rawlings@wycliffe.ca>
alex-rawlings-yyc added a commit that referenced this pull request May 21, 2026
* Add interlinearizer project storage and createProject command

- Add `src/projectStorage.ts` with `createProject`, `getProject`, `listProjects`, and `deleteProject` backed by `papi.storage`
- Register `interlinearizer.createProject` command in `main.ts` that prompts for source/target projects, writes the record, and surfaces storage errors as notifications
- Add `InterlinearProject` type and `interlinearizer.createProject` signature to shared type declarations
- Extend PAPI backend mock with `papi.storage` and `papi.notifications`
- Broaden Jest coverage to all `src/**` files
- Add full test suites for the storage module and the new command

* Documentation improvement

* Documentation improvements, improve error handling when deleting project and add associated test, fix schema inconsistency

* Set `restoreMocks` to `true` in jest config

* Make sure notification failure doesn't cause unnecessary throw, reorder index/data write to `papi` storage to ensure data doesn't become inaccessible

* Prevent same-project selection when creating an interlinear project, update mocks to match real API signatures

- If the user picks the same project for both source and target roles, a warning notification is shown and the target picker re-opens until distinct projects are chosen or cancelled.
- Includes new tests for this flow, updated platform-bible-react/utils mocks to match current API shapes, a new localized error string, and minor test/parser cleanups.

* Add missing `localizedStrings`, add clarifying comment, improve test coverage

* Improve docstring coverage

* Add project creation, metadata editing, and project selection UI

Introduces three modal components (CreateProjectModal, ProjectMetadataModal, SelectInterlinearProjectModal) with supporting menu contributions, localized strings, command registrations in main.ts, storage helpers in projectStorage.ts, and updated type definitions. Adds full test coverage for all new components and expands existing test suites to cover the new flows.

* Fix modal close/callback ordering and trim whitespace from language input

- `CreateProjectModal`: guard `onClose()` behind a `!newId` early-return so the modal doesn't close when project creation returns no ID
- `ProjectMetadataModal`: trim whitespace from the language field before saving and passing to callbacks
- Replace `makeHandleProjectCreated` factory with a plain `handleProjectCreated` callback; removes the closure-over-srcId pattern and reads `projectId` from the enclosing scope directly

* Return full project JSON from createProject and add error handling for delete/update

- `interlinearizer.createProject` now returns the full persisted project as a JSON string instead of just the UUID, so the WebView can populate `activeProject` with authoritative server data rather than reconstructing it locally
- `CreateProjectModal.onProjectCreated` callback now receives the parsed `InterlinearProjectSummary` object instead of `(id, writingSystem)` pair
- Add `isInterlinearProjectSummary` type guard to `SelectInterlinearProjectModal` and reuse it in the project list filter and the new create flow
- Wrap `deleteProject` and `updateProjectMetadata` backend handlers in try/catch with logging and error notifications (previously unhandled rejections)
- Register a no-op `interlinearizer.viewProjectInfo` backend command so the platform menu system can surface it; all logic runs in the WebView
- Update tests and type declarations to match

* Use `<dialog>` for modals, guard on falsy update return, and relax gloss/senseRef constraint

- Replace `<div>` containers with `<dialog open aria-labelledby="…">` in `CreateProjectModal`, `ProjectMetadataModal`, and `SelectInterlinearProjectModal` for proper accessibility semantics.
- In `ProjectMetadataModal.handleSave`, return early when `updateProjectMetadata` resolves with a falsy value (mirrors the existing guard in `CreateProjectModal.handleCreate`).
- In `interlinearizer.d.ts`, replace the discriminated union on `Token` and `Phrase` that forbade setting both `gloss`/`glossSenseRef` (or `gloss`/`senseRef`) with optional fields, reflecting the updated rule that `gloss` serves as a local override when both are present.
- Add a test for the falsy-return early-exit path in `ProjectMetadataModal`, and add the missing `waitFor` before negative assertions in the `CreateProjectModal` falsy-return test.

* Fix analysis language default, button state, and storage error propagation

- Default analysis language to "und" (undetermined) instead of "en"
- Normalize whitespace-only language input to "und" on submit
- Disable the create button while submission is in progress
- Rethrow storage errors in getProjectsForSource so callers can distinguish an outage from an empty list

* Throw on malformed createProject response instead of silently skipping onProjectCreated

Previously, if the backend returned a JSON object that failed isInterlinearProjectSummary, the modal would call onClose() without invoking onProjectCreated, silently dropping the contract. Now it throws, routing through the existing error handler. Also fixes an incomplete fixture in the success test that was missing required summary fields.

* Cleanup after incredibly messy rebase

* More post-rebase cleanup

* Improve command registration usage and sanity

* Disable cancel during submission to avoid "I cancelled but the project was still created", update docs

* Move `projectStorage` to `services` directory

* Improve project creation/update error handling, improve project load validation

* Improve `projectStorage` error-handling and documentation

* Add missing `\@throws` docstring

* Rename modal control commands for clarity, fix handlers in InterlinearizerLoader

* Improve docs/schema description

* Split modals into separate component (#62)

Co-authored-by: alex-rawlings-yyc <alex.rawlings@wycliffe.ca>

* Remove TOCTOU race from PAPI storage writes, remove duplicate notifications on error, disable metadata modal buttons when submitting

* Add eslint ignore, add submitting ref

* Add missing docs

* Send notification when created project has unexpected shape, add submitting ref

* Validate name and description in type guard

* Prevent project write race, add aria prop

* Disable buttons while submitting/loading, update docs, make implicit zero-check in ContinuousView explicit

* Skip and log singleton corrupted project records

* Update remaining `tw-` tags

* Extract modal logic into ProjectModals, tighten type guards and coverage

- Add ProjectModals component to own modal state (select/create/metadata) and their transitions, removing this responsibility from InterlinearizerLoader
- Add ProjectModals.test.tsx with full coverage of modal visibility and transition flows
- Strengthen isPlatformError mock to require platformErrorVersion to be a valid number, not just present
- Handle SyntaxError in CreateProjectModal.handleSubmit with a user-visible notification instead of silently failing
- Add test for invalid JSON response in CreateProjectModal
- Add test for non-string description field in SelectInterlinearProjectModal
- Mark isSubmittingRef guard branches with v8 ignore in modals

* Update docs, fix misleading test

* Align `atStart` and `atEnd` length checks, get rid of `createSourceIsSelect`, add function to reset storage queues for testing

* Fix and consolidate modal styles (#74)

* Simplify interlinear model (#63)

* Simplify interlinear model: remove InterlinearAlignment/InterlinearText, add ActiveProject

* Fix model gaps for lossless LCM / PT9 / BT Extension import

* Make `analysisLanguages` required

* Add comments about mapping of BT Extension's `sideNum`

* Update docs/schema

* Further refinement; please see updated description

* Suggested model tweak

* Model idea: Split linking out from analyses

---------

Co-authored-by: D. Ror. <imnasnainaec@gmail.com>

* Align code with simplified interlinear model

Update components, storage, main, and types to match the model introduced in #63: rename commands, fix return types, update JSDoc, and adjust tests throughout.

* Memoize Modal handler callbacks, add `targetProjectId` to modals where relevant to prevent silent deletion, docstring audit

* Add missing JSDocs

* Align stub with actual implementation, send notification when save/submission fails, docs adjustments

* Trimmed down `AGENTS.md` to where it was before post-model-change update

* Prevent stale data race condition, update old TW classes, TW class consolidation

* Prevent double notifications

* Improve docs, minor fixes

* Add aria modal tag where missing

* Post-rebase cleanup

* Make `webViewNonce` optional, revert to earlier TW classes, introduce missing TW classes

* Fix lint error

* Fix Tailwind classes, clean up test files, ignore branches that don't need testing, reintroduce createSourceIsSelect to reopen SelectProjectModal if user cancels project creation

* Fix lint issue

* Fix test failure

---------

Co-authored-by: Danny Rorabaugh <imnasnainaec@gmail.com>
@coderabbitai coderabbitai Bot mentioned this pull request Jul 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🟪Idea Idea-priority PR: can be closed...

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants