Skip to content

fix: preserve onboarding resume state on reload#1941

Merged
Ajit-Mehrotra merged 3 commits intomainfrom
codex/onboarding-indexing-continuing
Mar 20, 2026
Merged

fix: preserve onboarding resume state on reload#1941
Ajit-Mehrotra merged 3 commits intomainfrom
codex/onboarding-indexing-continuing

Conversation

@Ajit-Mehrotra
Copy link
Contributor

@Ajit-Mehrotra Ajit-Mehrotra commented Mar 20, 2026

Summary

  • remove currentStepIndex from onboarding draft persistence and resume logic so the flow only relies on currentStepId
  • preserve a resumed ACTIVATE_LICENSE step while activation bootstrap data is still loading so the modal does not jump forward to SUMMARY on reload
  • add resume coverage across the onboarding steps and the license-step loading race

Problem

When a user closed the tab while onboarding was paused on ACTIVATE_LICENSE, the saved draft still contained currentStepId: "ACTIVATE_LICENSE", but reopening the tab could land them on SUMMARY instead of the license step.

This was happening consistently because the modal rebuilt its visible steps before activation state had finished loading. During that window, ACTIVATE_LICENSE was temporarily excluded from the step list, the modal fell back to the nearest visible step, and that fallback rewrote the saved draft to SUMMARY.

Root Cause

  • onboarding was persisting both currentStepId and currentStepIndex, even though the ID is the real source of truth
  • the modal remapped saved steps when the currently persisted step was not visible
  • on reload, activation state could briefly make ACTIVATE_LICENSE appear unavailable, which triggered the remap and overwrote the saved step

Changes

  • remove currentStepIndex from the onboarding draft store API and persisted payload
  • ignore the legacy currentStepIndex field when deserializing existing onboarding drafts
  • update onboarding navigation syncing to persist only the active StepId
  • keep ACTIVATE_LICENSE in the visible step list during activation bootstrap loading when the persisted step being resumed is the license step
  • update onboarding tests to cover ID-only step persistence, storage cleanup, and resume behavior for each step

Testing

  • pnpm --dir web test -- --run web/__test__/components/Onboarding/OnboardingModal.test.ts web/__test__/components/Onboarding/onboardingStorageCleanup.test.ts web/__test__/store/onboardingModalVisibility.test.ts

Summary by CodeRabbit

  • Bug Fixes

    • License activation step remains visible while activation data is loading, preventing premature navigation.
  • Refactor

    • Onboarding now tracks the current step by ID instead of by index, improving persistence and resume behavior.
  • Tests

    • Updated and added tests covering ID-based step persistence, resumed activation behavior during loading, and storage cleanup.

- Purpose: make onboarding resume logic rely only on persisted step IDs and preserve the saved Activate License step across reloads.
- Before this change: the onboarding draft persisted both currentStepId and currentStepIndex, and the modal could remap a saved ACTIVATE_LICENSE step to SUMMARY while activation data was still loading.
- Why that was a problem: users who closed and reopened the tab during onboarding could land on the wrong screen even though the draft still pointed at the license step.
- What this change accomplishes: onboarding resume now uses only currentStepId, ignores the legacy index field, and keeps the activation step available during the bootstrap loading window so the saved step is not rewritten incorrectly.
- How it works: the draft store no longer exposes or restores currentStepIndex, the modal only syncs step IDs, and the onboarding modal tests now cover step-by-step resume behavior plus the activation-loading race.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 20, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: a36e808d-4fe7-4543-b4d6-54e8c8c08759

📥 Commits

Reviewing files that changed from the base of the PR and between 8b753dd and 0bf59db.

📒 Files selected for processing (1)
  • web/__test__/components/Onboarding/OnboardingModal.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • web/test/components/Onboarding/OnboardingModal.test.ts

Walkthrough

Switched onboarding state from index-based to ID-based: removed currentStepIndex, updated setCurrentStep(stepId), and ignored legacy persisted currentStepIndex. OnboardingModal now reads activationCodeDataStore.loading and can keep the ACTIVATE_LICENSE step visible while activation data is loading.

Changes

Cohort / File(s) Summary
Core Store Refactoring
web/src/components/Onboarding/store/onboardingDraft.ts
Removed currentStepIndex state and persistence. Changed setCurrentStep(stepId, index)setCurrentStep(stepId). Strips legacy currentStepIndex during deserialization.
Component Logic Update
web/src/components/Onboarding/OnboardingModal.vue
Reads activationCodeDataStore.loading; added shouldKeepResumedActivationStep logic. Removed index-based draft usage; rely on currentStepId and currentDynamicStepIndex for bounds. Simplified watchEffect to persist by ID.
Onboarding Component Tests
web/__test__/components/Onboarding/OnboardingModal.test.ts
Updated mocks to include activationCodeDataStore.loading and changed setCurrentStep mock signature. Removed currentStepIndex assignments. Added parameterized rendering tests and an async test ensuring ACTIVATE_LICENSE remains visible during activation loading.
Storage & Visibility Tests
web/__test__/components/Onboarding/onboardingStorageCleanup.test.ts, web/__test__/store/onboardingModalVisibility.test.ts
Updated local/session storage fixtures to persist currentStepId instead of currentStepIndex. Adjusted test calls and removed index-reset assertions.

Sequence Diagram

sequenceDiagram
    participant User
    participant OnboardingModal
    participant DraftStore as onboardingDraft
    participant ActivationStore as activationCodeDataStore
    participant StepComponent

    User->>OnboardingModal: open or resume onboarding
    OnboardingModal->>DraftStore: restore persisted state
    DraftStore-->>OnboardingModal: currentStepId
    OnboardingModal->>ActivationStore: read loading & activation state
    ActivationStore-->>OnboardingModal: loading = true, activationState = null

    alt activation data loading
        OnboardingModal->>OnboardingModal: shouldKeepResumedActivationStep = true
        OnboardingModal->>StepComponent: render ACTIVATE_LICENSE
        StepComponent-->>User: show activation step
        ActivationStore->>OnboardingModal: loading = false, activationState available
        OnboardingModal->>OnboardingModal: recompute visibility
    else activation data loaded
        OnboardingModal->>DraftStore: setCurrentStep(currentStepId)
        DraftStore-->>OnboardingModal: acknowledge
        OnboardingModal->>StepComponent: render appropriate step
        StepComponent-->>User: display step
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I hopped from indexes into bright IDs,
Kept the activation step while data breathes,
Tests were tuned and storage switched with glee,
Steps resume by name — not number — free,
A carrot-coded cheer for tidy deeds! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: preserve onboarding resume state on reload' is fully related to the main change: the PR removes index-based persistence, treats stepId as sole source of truth, and preserves the ACTIVATE_LICENSE step during activation loading—directly addressing the reload/resume state problem described in objectives.

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

✨ 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 codex/onboarding-indexing-continuing

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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/__test__/components/Onboarding/OnboardingModal.test.ts`:
- Around line 41-44: The test uses mocked storeToRefs which returned plain {
value } objects so mutations don't trigger reactivity; update the test to create
real Vue refs for loading, hasActivationCode and registrationState (import ref
from 'vue' and use ref(false), ref(true), ref<string|null>('ENOKEYFILE')
respectively), ensure registrationState is typed as string | null so assigning
null in the test is allowed, and replace the plain objects with these refs so
changing .value will retrigger the component and exercise the loading→loaded
transition (references: storeToRefs mock, loading, hasActivationCode,
registrationState in OnboardingModal.test.ts).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: fb1b108b-31a3-460b-a069-89cb2714b862

📥 Commits

Reviewing files that changed from the base of the PR and between f0241a8 and b4ad121.

📒 Files selected for processing (5)
  • web/__test__/components/Onboarding/OnboardingModal.test.ts
  • web/__test__/components/Onboarding/onboardingStorageCleanup.test.ts
  • web/__test__/store/onboardingModalVisibility.test.ts
  • web/src/components/Onboarding/OnboardingModal.vue
  • web/src/components/Onboarding/store/onboardingDraft.ts

@codecov
Copy link

codecov bot commented Mar 20, 2026

Codecov Report

❌ Patch coverage is 88.88889% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 51.55%. Comparing base (f0241a8) to head (0bf59db).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
...src/components/Onboarding/store/onboardingDraft.ts 33.33% 2 Missing ⚠️
web/src/components/Onboarding/OnboardingModal.vue 95.83% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1941      +/-   ##
==========================================
+ Coverage   51.51%   51.55%   +0.04%     
==========================================
  Files        1025     1025              
  Lines       70460    70460              
  Branches     7793     7800       +7     
==========================================
+ Hits        36297    36329      +32     
+ Misses      34040    34008      -32     
  Partials      123      123              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

- Purpose: keep the onboarding modal test fixtures aligned with the nullable registration state used by the activation store.
- Before this change: the new activation-loading resume test assigned  to a mock that was inferred as a non-null string.
- Why that was a problem: workspace type-check failed even though the runtime behavior and assertions were correct.
- What the new change accomplishes: the mock now reflects the real  shape and allows the loading-state test to compile cleanly.
- How it works: the hoisted  fixture is explicitly typed as , which matches the store contract used by the component.
- Purpose: make the onboarding modal loading-transition test behave like the real Pinia store refs used at runtime.
- Before this change: the test mutated plain value-holder objects after mount while storeToRefs was mocked to return the store object directly.
- Why that was a problem: the test could pass without actually proving that the component reacts to the activation loading-to-loaded transition.
- What the new change accomplishes: the mocked activation loading, activation-code presence, and registration state are now real Vue refs during each test run.
- How it works: beforeEach recreates those mocked store fields with ref(...) values, which lets post-mount value changes retrigger the component and satisfies lint, type-check, and the review feedback.
@github-actions
Copy link
Contributor

This plugin has been deployed to Cloudflare R2 and is available for testing.
Download it at this URL:

https://preview.dl.unraid.net/unraid-api/tag/PR1941/dynamix.unraid.net.plg

@Ajit-Mehrotra Ajit-Mehrotra marked this pull request as ready for review March 20, 2026 20:03
@Ajit-Mehrotra Ajit-Mehrotra requested a review from elibosley March 20, 2026 21:14
@Ajit-Mehrotra Ajit-Mehrotra merged commit 91f7fe9 into main Mar 20, 2026
13 checks passed
@Ajit-Mehrotra Ajit-Mehrotra deleted the codex/onboarding-indexing-continuing branch March 20, 2026 21:18
elibosley pushed a commit that referenced this pull request Mar 23, 2026
🤖 I have created a release *beep* *boop*
---


## [4.31.0](v4.30.1...v4.31.0)
(2026-03-23)


### Features

* **api:** support encrypted array start inputs
([#1944](#1944))
([018a8d5](018a8d5))
* **onboarding:** add shared loading states
([#1945](#1945))
([776c8cc](776c8cc))
* Serverside state for onboarding display
([#1936](#1936))
([682d51c](682d51c))


### Bug Fixes

* **api:** reconcile emhttp state without spinning disks
([#1946](#1946))
([d3e0b95](d3e0b95))
* **onboarding:** auto-open incomplete onboarding on 7.3+
([#1940](#1940))
([f0241a8](f0241a8))
* **onboarding:** replace internal boot native selects
([#1942](#1942))
([d6ea032](d6ea032))
* preserve onboarding resume state on reload
([#1941](#1941))
([91f7fe9](91f7fe9))
* recover VM availability after reconnect
([#1947](#1947))
([e064de7](e064de7))
* Unify callback server payloads
([#1938](#1938))
([f58fcc0](f58fcc0))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
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.

2 participants