feat: Guided Tours Ephemeral Pipelines#2334
Conversation
🎩 PreviewA preview build has been created at: |
|
Warning This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
This stack of pull requests is managed by Graphite. Learn more about stacking. |
ab7dd0b to
8eb1ea3
Compare
fb58c22 to
a71461e
Compare
151abae to
3b86f76
Compare
774e9d6 to
f3ba10d
Compare
802ddf3 to
fed719d
Compare
f3ba10d to
6f56eba
Compare
fed719d to
288fe40
Compare
6f56eba to
07be379
Compare
1ce8343 to
e88ba69
Compare
4479f54 to
e7bee79
Compare
d009f6a to
7dd3ef7
Compare
e223594 to
d640e0a
Compare
cdf8d07 to
d542c2d
Compare
b83942f to
a52076f
Compare
| const existingPipelineNames = new Set( | ||
| Array.from(userPipelines.keys()).map((name) => name.toLowerCase()), | ||
| ); | ||
| const error = useMemo(() => { |
There was a problem hiding this comment.
NIT (Follow up) here and everywhere - better to rely on React Compiler and avoid useMemo/useCallback if possible
|
|
||
| import { TourPipelineStorageService } from "./TourPipelineStorageService"; | ||
|
|
||
| export function TourPipelineStorageProvider({ |
There was a problem hiding this comment.
🤖 This is an AI-generated code review comment.
This PR adds a new React component under src/providers, but react-compiler.config.js does not enable src/providers/TourProvider/tourPipelineStorage or this file specifically. The project standard requires new components/hooks to be covered by React Compiler unless they live under an already-enabled directory; src/providers is still commented out in the config.
Suggestion: Add src/providers/TourProvider/tourPipelineStorage/TourPipelineStorageProvider.tsx to REACT_COMPILER_ENABLED_DIRS, or enable the containing directory if all files there are compiler-compatible.
Rule: .claude/skills/react-patterns/SKILL.md — React Compiler: new files/components must be added to react-compiler.config.js; tangle-ui-review checklist §1

Description
Moves tour pipelines out of the durable IndexedDB store and into session storage, so they are truly ephemeral. Before this change, a tour-pipeline lived alongside the user's real pipelines: it showed up in the pipelines list, contended for unique names, and survived browser refresh — none of which is what we want from a guided demo.
The fix is a scoped storage override that only applies while the user is on
/tour/$tourId. Inside the tour route,usePipelineStorage()returns aTourPipelineStorageServicebacked bysessionStorage; everywhere else, it still returns the durable IndexedDB service. The autosave path, the spec loader, and the editor's storage consumers don't need to know which one they're talking to.What changed
New scoped storage layer (
src/providers/TourProvider/tourPipelineStorage/)SessionStoragePipelineDriver.ts— driver that reads/writes pipeline YAML againstsessionStoragekeys, matching the shape the existingPipelineStorageServiceexpects from a driver.TourPipelineFolder.ts— single root folder that holds the tour pipeline, withcanMoveFilesOut: falseetc. so the rest of the app can't accidentally relocate it.TourPipelineStorageService.ts—PipelineStorageServicesubclass whoserootFolder,resolvePipelineByName, etc. all go through the session-storage driver.TourPipelineStorageProvider.tsx— context override that the tour route wraps its body in. Inside,usePipelineStorage()resolves to the tour service.resetAllTourPipelineState.ts— clears every__tour__*key from session storage and evicts matching entries from the react-query spec cache. Called when starting any tour so each run begins clean.Tour route plumbing (
src/routes/Dashboard/Learn/Tour.tsx)TourPageis split intoTourPage(resolves the tour, guards on!tour) andTourPageBody(does the pipeline resolution and rendering). The split exists soTourPipelineStorageProvidercan wrap the body —usePipelineStorage()insideTourPageBodythen resolves to the tour storage.createTourPipelineis renamedfindOrCreateTourPipelineto reflect that browser refresh mid-tour reuses the existing session-storage file (so the tour resumes on the same step with edits intact). Re-entering the tour from the Learn page still starts fresh —FeaturedTours/ToursLibrarycallresetAllTourPipelineStatebefore navigating, which wipes session storage and the react-query spec cache.Pipeline registry orphan fix (
src/services/pipelineService.ts)deletePipelineremoved the file from its folder driver but didn't delete the corresponding entry from the pipeline registry. The orphaned registry entry caused later "name already exists" errors for tour pipelines whose files had been cleared but whose registry rows lived on. Now both are deleted.Name-dialog race fix (
src/components/shared/Dialogs/PipelineNameDialog.tsx)setErroreffect that didn't always settle before the user clicked Submit, occasionally letting through a name that should have been rejected. Rewrote it as auseMemoso the error is always in sync with the current input. Also addedexcludeNamesso a rename-to-self in the regular editor doesn't flag itself as a collision.Why scoped override rather than a flag
Threading a
isTour: trueflag through every storage call would have leaked tour-awareness into a layer that doesn't need it. The provider override is opaque to consumers — sameusePipelineStorage(), samePipelineFileshape, different backing store.Related Issue and Pull requests
Progresses https://github.com/Shopify/oasis-frontend/issues/583
Type of Change
Checklist
Test Instructions
Pre-req: at least one registered tour exists (so use this branch with #2306 / #2312 on top, or open a Storybook-style local entry).
Ephemerality
?step=N) and the edit you made is still there (session storage survives same-tab refresh, as intended).Name-collision regression
/learn/tours. No "Name already exists" error —resetAllTourPipelineStateclears the prior run.Registry orphan regression
Regression