Skip to content

Keep recipe generation alive across tab switches#116

Merged
jschoedl merged 3 commits into
mainfrom
feature/persist-generation-across-tabs
Jul 2, 2026
Merged

Keep recipe generation alive across tab switches#116
jschoedl merged 3 commits into
mainfrom
feature/persist-generation-across-tabs

Conversation

@jschoedl

@jschoedl jschoedl commented Jun 25, 2026

Copy link
Copy Markdown
Collaborator

Summary

During recipe generation, switching to another tab (e.g. Library/Profile) used to lose the in-flight request: returning back to /generate showed an empty list.

This PR moves the generation state (prompt, selectedTags, recipes, status, loading) and the generate() request out of GenerateFlow into a new RecipeGenerationProvider mounted above AppLayout. The provider stays mounted across tab switches, so a response that arrives while you're on another tab is kept and shown when you navigate back, and the loading state is preserved mid-flight.

Type of change

  • Bug fix
  • New feature
  • Refactor / cleanup
  • Infrastructure / CI
  • Documentation

API changes

  • This PR does not affect the API
  • This PR changes the API → api/openapi.yaml updated and api/scripts/gen-all.sh re-run

Definition of Done

  • CI passes
  • Pre-commit hooks pass locally
  • Relevant tests added or updated

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Recipe-generation state now persists across the generate, results, and recipe-browsing views, so your prompt and preferences remain available while navigating.
  • Bug Fixes
    • Improved generated-recipe browsing behavior, making switching between recipes more reliable.
    • Enhanced loading/status handling and session-related behavior during recipe generation.
  • Tests
    • Updated generate and recipe page tests to reflect the new shared generation state across routes.

Move the generation state and request out of the GenerateFlow route into
a RecipeGenerationProvider mounted above AppLayout. The /generate route
unmounts on tab switch, which orphaned the in-flight request and its state
updates; the provider stays mounted across tabs, so results that arrive
while on another tab are kept and shown on return.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jun 25, 2026

Copy link
Copy Markdown

🧹 The preview environment was removed because this PR was closed.

@jschoedl jschoedl linked an issue Jun 25, 2026 that may be closed by this pull request
2 tasks
@jschoedl jschoedl requested review from imol-ai and paulwiese June 25, 2026 19:17
@imol-ai

imol-ai commented Jun 26, 2026

Copy link
Copy Markdown
Collaborator

Hmm, I've tested it on both chrome and firefox, but it works for me on neither.

@jschoedl

Copy link
Copy Markdown
Collaborator Author

@imol-ai Note that when jumping back to the recipes page, you always see the tags first, but now there should be the "view recipes" button at the top right as soon as it's done.

@imol-ai

imol-ai commented Jun 27, 2026

Copy link
Copy Markdown
Collaborator

Woops, sorry, completely missed that - I just expected it to be the default view on the page. Looks good!

…ation-across-tabs

# Conflicts:
#	web-client/src/pages/GeneratePage.tsx
@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9451b261-be9c-4f5c-ae6d-dc5e2993eb12

📥 Commits

Reviewing files that changed from the base of the PR and between 9edfc30 and f8333f4.

📒 Files selected for processing (2)
  • web-client/src/recipeGeneration.tsx
  • web-client/tests/pages/GeneratePage.test.tsx
✅ Files skipped from review due to trivial changes (1)
  • web-client/tests/pages/GeneratePage.test.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • web-client/src/recipeGeneration.tsx

📝 Walkthrough

Walkthrough

A new shared recipe-generation provider centralizes prompt, tag, recipe, status, and loading state. The app and recipe-related pages now read from that provider instead of outlet context, and the tests were updated to render under the new provider and use sessionStorage-backed recipe data.

Changes

Recipe Generation Context Refactor

Layer / File(s) Summary
RecipeGenerationProvider implementation and hook
web-client/src/recipeGeneration.tsx
New module defines RecipeGenerationContextValue, RecipeGenerationProvider with sessionStorage-backed state and generate() action, useRecipeGeneration hook, and Recipe type.
App-level provider wiring
web-client/src/App.tsx
Imports and wraps AppLayout with RecipeGenerationProvider inside the RequireAuth route element.
GenerateFlow/GeneratePage migration
web-client/src/pages/GeneratePage.tsx
Removes exported RecipeGenerationContext interface and local generation logic; GenerateFlow narrows to session validation and renders bare Outlet; GeneratePage/GenerateResultsPage consume useRecipeGeneration.
RecipePage migration
web-client/src/pages/RecipePage.tsx
Removes useOutletContext/RecipeGenerationContext usage; GeneratedRecipePage now pulls recipes/setRecipes from useRecipeGeneration.
Test updates
web-client/tests/pages/GeneratePage.test.tsx, web-client/tests/pages/RecipePage.test.tsx
Tests wrap routes with RecipeGenerationProvider, seed sessionStorage with generated recipes, and clear sessionStorage after each test.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant GeneratePage
  participant RecipeGenerationProvider
  participant Backend

  GeneratePage->>RecipeGenerationProvider: generate()
  RecipeGenerationProvider->>RecipeGenerationProvider: set loading, clear recipes, persist prompt/tags
  RecipeGenerationProvider->>GeneratePage: navigate to /generate/results
  RecipeGenerationProvider->>Backend: POST /ai/recipes
  Backend-->>RecipeGenerationProvider: response
  RecipeGenerationProvider->>RecipeGenerationProvider: update recipes/status, persist to sessionStorage
  RecipeGenerationProvider->>RecipeGenerationProvider: clear loading (finally)
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: preserving recipe generation state when switching tabs.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/persist-generation-across-tabs

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.

@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.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
web-client/tests/pages/GeneratePage.test.tsx (1)

29-32: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Missing sessionStorage.clear() in afterEach — test isolation gap.

Test 1 seeds sessionStorage['generated_recipes'] but nothing here clears it afterward, unlike the sibling RecipePage.test.tsx which added sessionStorage.clear() to its afterEach in this same cohort. Currently masked because generate() always resets recipes to [] synchronously, but any future test asserting on a clean initial recipe list will be order-dependent/flaky.

🧹 Proposed fix
 afterEach(() => {
 	fetchMock.mockReset()
 	vi.unstubAllGlobals()
+	sessionStorage.clear()
 })
🤖 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 `@web-client/tests/pages/GeneratePage.test.tsx` around lines 29 - 32, The
GeneratePage test cleanup is missing sessionStorage reset, so seeded
generated_recipes can leak into later tests and create order-dependent behavior.
Update the afterEach in GeneratePage.test.tsx to also clear sessionStorage,
matching the sibling RecipePage.test.tsx cleanup, alongside
fetchMock.mockReset() and vi.unstubAllGlobals() so each test starts with a clean
storage state.
🧹 Nitpick comments (1)
web-client/src/recipeGeneration.tsx (1)

47-56: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Persist prompt/selectedTags consistently with recipes.

recipes is synced to sessionStorage on every state change (Lines 47-49), but prompt/selectedTags are only written at the moment generate() fires (Lines 55-56). Typed input or tag selections that haven't triggered a generation are lost on a full page refresh, unlike recipes.

♻️ Proposed fix
+useEffect(() => {
+	sessionStorage.setItem('recipe_prompt', prompt)
+}, [prompt])
+
+useEffect(() => {
+	sessionStorage.setItem('recipe_tags', JSON.stringify(selectedTags))
+}, [selectedTags])
+
 useEffect(() => {
 	sessionStorage.setItem('generated_recipes', JSON.stringify(recipes))
 }, [recipes])

And drop the now-redundant writes inside generate() (Lines 55-56).

🤖 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 `@web-client/src/recipeGeneration.tsx` around lines 47 - 56, The
`recipeGeneration` state persistence is inconsistent: `recipes` is kept in
sessionStorage via the existing `useEffect`, but `prompt` and `selectedTags` are
only saved inside `generate()`. Add sessionStorage sync for `prompt` and
`selectedTags` alongside the `recipes` effect in `recipeGeneration.tsx`, and
remove the redundant writes from `generate()` so `generate`, `useEffect`, and
the `recipe_prompt`/`recipe_tags` keys all stay in one persistence path.
🤖 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.

Inline comments:
In `@web-client/src/recipeGeneration.tsx`:
- Around line 35-43: The `RecipeGenerationProvider` state initializers for
`selectedTags` and `recipes` are parsing `sessionStorage` JSON unsafely, which
can now crash the whole app when mounted above `AppLayout`. Update the
`recipeGeneration.tsx` initial state logic to guard the `JSON.parse` calls with
try/catch or a safe parsing helper, and fall back to empty arrays when
`recipe_tags` or `generated_recipes` contain invalid JSON.

---

Outside diff comments:
In `@web-client/tests/pages/GeneratePage.test.tsx`:
- Around line 29-32: The GeneratePage test cleanup is missing sessionStorage
reset, so seeded generated_recipes can leak into later tests and create
order-dependent behavior. Update the afterEach in GeneratePage.test.tsx to also
clear sessionStorage, matching the sibling RecipePage.test.tsx cleanup,
alongside fetchMock.mockReset() and vi.unstubAllGlobals() so each test starts
with a clean storage state.

---

Nitpick comments:
In `@web-client/src/recipeGeneration.tsx`:
- Around line 47-56: The `recipeGeneration` state persistence is inconsistent:
`recipes` is kept in sessionStorage via the existing `useEffect`, but `prompt`
and `selectedTags` are only saved inside `generate()`. Add sessionStorage sync
for `prompt` and `selectedTags` alongside the `recipes` effect in
`recipeGeneration.tsx`, and remove the redundant writes from `generate()` so
`generate`, `useEffect`, and the `recipe_prompt`/`recipe_tags` keys all stay in
one persistence path.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 104959c1-2b47-4459-a5c7-68b59aa7e1cf

📥 Commits

Reviewing files that changed from the base of the PR and between ddeedef and 9edfc30.

📒 Files selected for processing (6)
  • web-client/src/App.tsx
  • web-client/src/pages/GeneratePage.tsx
  • web-client/src/pages/RecipePage.tsx
  • web-client/src/recipeGeneration.tsx
  • web-client/tests/pages/GeneratePage.test.tsx
  • web-client/tests/pages/RecipePage.test.tsx

Comment thread web-client/src/recipeGeneration.tsx Outdated
@jschoedl jschoedl merged commit 33ad46d into main Jul 2, 2026
6 checks passed
@jschoedl jschoedl deleted the feature/persist-generation-across-tabs branch July 2, 2026 16:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Keep generating on tab switch

2 participants