Skip to content

fix(desktop): keep onboarding skip visible during loading#4175

Merged
saddlepaddle merged 1 commit into
mainfrom
onboarding-projects-skip
May 7, 2026
Merged

fix(desktop): keep onboarding skip visible during loading#4175
saddlepaddle merged 1 commit into
mainfrom
onboarding-projects-skip

Conversation

@saddlepaddle
Copy link
Copy Markdown
Collaborator

@saddlepaddle saddlepaddle commented May 7, 2026

Summary

  • Every setup step (providers, gh-cli, permissions, project, adopt-worktrees) short-circuited to a full-screen spinner while its initial query was pending, hiding the "Skip for now" CTA. The project step was hit hardest because its underlying Electric v2_projects shape has to wait behind ~25 org collections warming up at signin — users with no escape from the spinner reported feeling stuck.
  • Each loading branch now renders inside the existing StepShell with the skip link still surfaced, so users always have a way out.
  • Swapped the project step from a useLiveQuery over collections.v2Projects to an imperative apiTrpcClient.v2Project.list query — the page only needs "do you have any projects?" and shouldn't pay the Electric warm-up cost for an answer used once.

Test plan

  • Sign in fresh and walk through onboarding; confirm "Skip for now" is visible on each step's loading state.
  • On the project step, verify the project list shows up via the new trpc call (with and without existing projects in the org).
  • Click "Skip for now" from the project loading state — should jump to /setup/adopt-worktrees.
  • Verify the rest of the project step flow still works: select new repo, continue with current, back to permissions.

Summary by cubic

Keep “Skip for now” visible on all onboarding steps during loading so users aren’t stuck behind full-screen spinners. Also reduces load time on the project step by avoiding Electric shape warm‑up.

  • Bug Fixes

    • Render loading states inside StepShell with the skip CTA for providers, gh-cli, permissions, project, and adopt-worktrees.
  • Refactors

    • Replace useLiveQuery over collections.v2Projects with apiTrpcClient.v2Project.list via @tanstack/react-query, fetching only the needed project presence to avoid ~25 org collections warm-up.

Written for commit 471c44f. Summary will update on new commits.

Summary by CodeRabbit

  • Improvements
    • Setup steps now display descriptive loading states with step headers and status information instead of blank spinners.
    • Added visible "Skip for now" options throughout the setup process for easier navigation.
    • Enhanced visual consistency and clarity across the onboarding wizard.

Every setup step short-circuited to a full-screen spinner while its
initial query was pending, hiding the skip CTA. Users who hit a slow
backing query (most often v2_projects on the project step) had no
escape. Each loading state now renders inside the StepShell with the
skip link still surfaced.

Also swaps the project step from a v2_projects live query to an
imperative v2Project.list trpc call so it doesn't gate on Electric
shape warm-up across all 25 org collections.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 7, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

This PR standardizes loading UI across five onboarding setup pages by replacing full-screen spinners with contextual StepShell/StepHeader layouts and skip buttons. Additionally, the project page migrates from local database queries to React Query TRPC API calls keyed by organization ID for improved data consistency and remote fetching.

Changes

Setup Flow Loading UI Standardization

Layer / File(s) Summary
React Query Data Fetching
apps/desktop/src/renderer/routes/_authenticated/setup/project/page.tsx
Project page imports useQuery and apiTrpcClient, retrieves active organization ID from session, and establishes a TRPC query to fetch projects keyed by organization with conditional enablement guard.
Loading UI Standardization
apps/desktop/src/renderer/routes/_authenticated/setup/adopt-worktrees/page.tsx, apps/desktop/src/renderer/routes/_authenticated/setup/gh-cli/page.tsx, apps/desktop/src/renderer/routes/_authenticated/setup/permissions/page.tsx, apps/desktop/src/renderer/routes/_authenticated/setup/project/page.tsx, apps/desktop/src/renderer/routes/_authenticated/setup/providers/page.tsx
All five setup pages replace full-screen centered spinners with StepShell/StepHeader layouts that display contextual status ("Looking for existing worktrees…", "Checking GitHub CLI…", etc.), centered spinners, and "Skip for now" buttons wired to existing skip handlers.
Skip Navigation Handlers
apps/desktop/src/renderer/routes/_authenticated/setup/project/page.tsx, apps/desktop/src/renderer/routes/_authenticated/setup/providers/page.tsx
Skip and continue navigation handlers remain unchanged; routing to subsequent setup steps is preserved.

🎯 2 (Simple) | ⏱️ ~12 minutes

Rabbits hop through setup flows with gleaming new UI,
Five pages dressed in matching StepShell attire.
Organization queries now fly through the TRPC wire,
Loading states spin prettily as worktrees and providers inquire—
✨ Onboarding refined, consistent, and spry! 🐰

🚥 Pre-merge checks | ✅ 4 | ❌ 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 (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix(desktop): keep onboarding skip visible during loading' accurately summarizes the primary fix: making the skip button visible during loading states across all onboarding steps.
Description check ✅ Passed The PR description is comprehensive and well-structured. It clearly explains the problem, the solution, and provides a detailed test plan. All key sections match the template structure with good detail.
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.

✏️ 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 onboarding-projects-skip

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.

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 7, 2026

Greptile Summary

This PR ensures "Skip for now" is always visible during onboarding by replacing full-screen spinners with StepShell-wrapped loading states across all five setup steps. The project step additionally switches from a useLiveQuery (Electric sync) to a direct apiTrpcClient tRPC call, avoiding the Electric warm-up cost for a one-time project existence check.

  • All five setup steps (providers, gh-cli, permissions, project, adopt-worktrees) now render their loading state inside the existing StepShell with consistent backTo and skip CTA.
  • The project step replaces useLiveQuery with useQuery({ enabled: !!activeOrganizationId, … }), which changes the loading semantics slightly: when activeOrganizationId is null the query is disabled and isLoading is false, briefly showing the empty state instead of a spinner.
  • The adopt-worktrees loading shell is missing maxWidth="lg" that the resolved content uses, causing a container width shift on transition.

Confidence Score: 4/5

Safe to merge; the UX improvement is straightforward and the two minor issues won't break any flows.

The adopt-worktrees width mismatch produces a visible layout shift but no functional breakage. The disabled-query edge case in the project step could momentarily show the empty repo selection view instead of a spinner, but the authenticated route guard makes it nearly impossible to hit in practice. All skip/navigate logic is correctly wired up and consistent across steps.

apps/desktop/src/renderer/routes/_authenticated/setup/project/page.tsx (isLoading vs disabled-query semantics) and apps/desktop/src/renderer/routes/_authenticated/setup/adopt-worktrees/page.tsx (missing maxWidth on loading shell)

Important Files Changed

Filename Overview
apps/desktop/src/renderer/routes/_authenticated/setup/project/page.tsx Switched project listing from useLiveQuery (Electric sync) to useQuery (direct tRPC call); loading state now shows inside StepShell with skip CTA. Edge case: when activeOrganizationId is null the query is disabled and isLoading is false, briefly showing the empty "Select a repository" view before the session resolves.
apps/desktop/src/renderer/routes/_authenticated/setup/adopt-worktrees/page.tsx Loading state now wrapped in StepShell with skip button; loading shell is missing maxWidth="lg" that the resolved AdoptWorktreesContent uses, causing a layout width shift on query resolution.
apps/desktop/src/renderer/routes/_authenticated/setup/providers/page.tsx Loading state moved inside StepShell with skip button; maxWidth="lg" is consistent with the resolved state.
apps/desktop/src/renderer/routes/_authenticated/setup/gh-cli/page.tsx Loading state moved inside StepShell with skip button; backTo and maxWidth are consistent with the resolved states.
apps/desktop/src/renderer/routes/_authenticated/setup/permissions/page.tsx Loading state moved inside StepShell with skip button; backTo and maxWidth="lg" are consistent with the resolved state.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[User lands on setup step] --> B{Query isPending?}
    B -- Yes --> C[StepShell loading state\nSpinner + Skip for now CTA]
    B -- No --> D{Has data?}
    C -- User clicks Skip --> E[markSkipped + navigate next]
    C -- Query resolves --> D
    D -- project step only\nactiveOrganizationId null --> F[Show empty Select a repo\nno loading state shown]
    D -- Data ready --> G[Full step UI]
    G --> H[Continue / Skip / Back]
Loading
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 2
apps/desktop/src/renderer/routes/_authenticated/setup/project/page.tsx:33-40
**Disabled query skips loading state when `activeOrganizationId` is null**

With `enabled: !!activeOrganizationId`, TanStack Query sets `isLoading: false` (not fetching) while the query is disabled. If `session?.session?.activeOrganizationId` resolves as `null`/`undefined` — even momentarily — the component falls straight through to the "Select a repository" view before transitioning to the loading state once the value becomes truthy. Consider using `isPending` instead of `isLoading`, or gating on `!session` as well, so the loading shell is shown for the full window between mount and the first successful fetch.

### Issue 2 of 2
apps/desktop/src/renderer/routes/_authenticated/setup/adopt-worktrees/page.tsx:74-91
The loading shell omits `maxWidth="lg"` while `AdoptWorktreesContent` passes it. When the query resolves, the container width jumps from the default to `lg`, producing a visible layout shift. Add the prop to keep the width consistent during the transition.

```suggestion
	if (isPending) {
		return (
			<StepShell backTo={STEP_ROUTES.project} maxWidth="lg">
				<StepHeader
					title="Looking for existing worktrees…"
					subtitle="Scanning your recent projects."
				/>
				<div className="flex justify-center py-2">
					<Spinner className="size-6 text-[#a8a5a3]" />
				</div>
				<div className="flex w-[273px] flex-col gap-2 self-center">
					<SetupButton variant="link" onClick={skipFlow}>
						Skip for now
					</SetupButton>
				</div>
			</StepShell>
		);
	}
```

Reviews (1): Last reviewed commit: "fix(desktop): keep onboarding "Skip for ..." | Re-trigger Greptile

Comment on lines +33 to +40
const { data: projects = [], isLoading } = useQuery({
queryKey: ["onboarding", "v2Projects", activeOrganizationId],
enabled: !!activeOrganizationId,
queryFn: () =>
apiTrpcClient.v2Project.list.query({
organizationId: activeOrganizationId as string,
}),
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Disabled query skips loading state when activeOrganizationId is null

With enabled: !!activeOrganizationId, TanStack Query sets isLoading: false (not fetching) while the query is disabled. If session?.session?.activeOrganizationId resolves as null/undefined — even momentarily — the component falls straight through to the "Select a repository" view before transitioning to the loading state once the value becomes truthy. Consider using isPending instead of isLoading, or gating on !session as well, so the loading shell is shown for the full window between mount and the first successful fetch.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/renderer/routes/_authenticated/setup/project/page.tsx
Line: 33-40

Comment:
**Disabled query skips loading state when `activeOrganizationId` is null**

With `enabled: !!activeOrganizationId`, TanStack Query sets `isLoading: false` (not fetching) while the query is disabled. If `session?.session?.activeOrganizationId` resolves as `null`/`undefined` — even momentarily — the component falls straight through to the "Select a repository" view before transitioning to the loading state once the value becomes truthy. Consider using `isPending` instead of `isLoading`, or gating on `!session` as well, so the loading shell is shown for the full window between mount and the first successful fetch.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines 74 to 91
if (isPending) {
return (
<div className="flex h-full w-full items-center justify-center bg-[#151110]">
<Spinner className="size-6 text-[#a8a5a3]" />
</div>
<StepShell backTo={STEP_ROUTES.project}>
<StepHeader
title="Looking for existing worktrees…"
subtitle="Scanning your recent projects."
/>
<div className="flex justify-center py-2">
<Spinner className="size-6 text-[#a8a5a3]" />
</div>
<div className="flex w-[273px] flex-col gap-2 self-center">
<SetupButton variant="link" onClick={skipFlow}>
Skip for now
</SetupButton>
</div>
</StepShell>
);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 The loading shell omits maxWidth="lg" while AdoptWorktreesContent passes it. When the query resolves, the container width jumps from the default to lg, producing a visible layout shift. Add the prop to keep the width consistent during the transition.

Suggested change
if (isPending) {
return (
<div className="flex h-full w-full items-center justify-center bg-[#151110]">
<Spinner className="size-6 text-[#a8a5a3]" />
</div>
<StepShell backTo={STEP_ROUTES.project}>
<StepHeader
title="Looking for existing worktrees…"
subtitle="Scanning your recent projects."
/>
<div className="flex justify-center py-2">
<Spinner className="size-6 text-[#a8a5a3]" />
</div>
<div className="flex w-[273px] flex-col gap-2 self-center">
<SetupButton variant="link" onClick={skipFlow}>
Skip for now
</SetupButton>
</div>
</StepShell>
);
}
if (isPending) {
return (
<StepShell backTo={STEP_ROUTES.project} maxWidth="lg">
<StepHeader
title="Looking for existing worktrees…"
subtitle="Scanning your recent projects."
/>
<div className="flex justify-center py-2">
<Spinner className="size-6 text-[#a8a5a3]" />
</div>
<div className="flex w-[273px] flex-col gap-2 self-center">
<SetupButton variant="link" onClick={skipFlow}>
Skip for now
</SetupButton>
</div>
</StepShell>
);
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/renderer/routes/_authenticated/setup/adopt-worktrees/page.tsx
Line: 74-91

Comment:
The loading shell omits `maxWidth="lg"` while `AdoptWorktreesContent` passes it. When the query resolves, the container width jumps from the default to `lg`, producing a visible layout shift. Add the prop to keep the width consistent during the transition.

```suggestion
	if (isPending) {
		return (
			<StepShell backTo={STEP_ROUTES.project} maxWidth="lg">
				<StepHeader
					title="Looking for existing worktrees…"
					subtitle="Scanning your recent projects."
				/>
				<div className="flex justify-center py-2">
					<Spinner className="size-6 text-[#a8a5a3]" />
				</div>
				<div className="flex w-[273px] flex-col gap-2 self-center">
					<SetupButton variant="link" onClick={skipFlow}>
						Skip for now
					</SetupButton>
				</div>
			</StepShell>
		);
	}
```

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Copy Markdown
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 current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/desktop/src/renderer/routes/_authenticated/setup/project/page.tsx`:
- Around line 33-40: The projects query currently only destructures data and
isLoading from useQuery, which hides failures; update the useQuery call that
calls apiTrpcClient.v2Project.list.query to also return isError, error, and
refetch (e.g., const { data: projects = [], isLoading, isError, error, refetch }
= useQuery(...)), then add an error state in the component that renders when
isError is true showing the error.message (or a fallback) and a retry control
that calls refetch; ensure the error text element includes the CSS classes
"select-text cursor-text" so the message is selectable.
🪄 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

Run ID: 0e5f8c73-5000-430b-9742-d8885eb4f2de

📥 Commits

Reviewing files that changed from the base of the PR and between 3a47475 and 471c44f.

📒 Files selected for processing (5)
  • apps/desktop/src/renderer/routes/_authenticated/setup/adopt-worktrees/page.tsx
  • apps/desktop/src/renderer/routes/_authenticated/setup/gh-cli/page.tsx
  • apps/desktop/src/renderer/routes/_authenticated/setup/permissions/page.tsx
  • apps/desktop/src/renderer/routes/_authenticated/setup/project/page.tsx
  • apps/desktop/src/renderer/routes/_authenticated/setup/providers/page.tsx

Comment on lines +33 to +40
const { data: projects = [], isLoading } = useQuery({
queryKey: ["onboarding", "v2Projects", activeOrganizationId],
enabled: !!activeOrganizationId,
queryFn: () =>
apiTrpcClient.v2Project.list.query({
organizationId: activeOrganizationId as string,
}),
});
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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n -C3 'data:\s*projects\s*=\s*\[\]|isError|error|refetch' apps/desktop/src/renderer/routes/_authenticated/setup/project/page.tsx

Repository: superset-sh/superset

Length of output: 546


🏁 Script executed:

cat -n apps/desktop/src/renderer/routes/_authenticated/setup/project/page.tsx | head -120

Repository: superset-sh/superset

Length of output: 5035


🏁 Script executed:

wc -l apps/desktop/src/renderer/routes/_authenticated/setup/project/page.tsx

Repository: superset-sh/superset

Length of output: 139


🏁 Script executed:

cat -n apps/desktop/src/renderer/routes/_authenticated/setup/project/page.tsx | tail -n +100

Repository: superset-sh/superset

Length of output: 2702


🏁 Script executed:

# Verify if useQuery has any default error handling or retry config visible
rg -n 'retry|onError|enabled' apps/desktop/src/renderer/routes/_authenticated/setup/project/page.tsx

Repository: superset-sh/superset

Length of output: 151


🏁 Script executed:

# Check if there's any global error handling or default config for useQuery in this file
rg -n 'useQuery|QueryClient|error|catch' apps/desktop/src/renderer/routes/_authenticated/setup/project/page.tsx

Repository: superset-sh/superset

Length of output: 223


Add error handling for the projects query to prevent silent failures.

Line 33 only destructures data and isLoading from useQuery, with no error handling. When the v2Project.list query fails, projects silently defaults to [], causing the UI to render "Select a repository" instead of showing an error. This masks query failures and can misroute users during onboarding.

Extract isError, error, and refetch from the query result and add an error state UI. As per coding guidelines, error text must be selectable with select-text cursor-text classes.

💡 Suggested fix
- const { data: projects = [], isLoading } = useQuery({
+ const {
+   data: projects,
+   isLoading,
+   isError,
+   error,
+   refetch,
+ } = useQuery({
    queryKey: ["onboarding", "v2Projects", activeOrganizationId],
    enabled: !!activeOrganizationId,
    queryFn: () =>
      apiTrpcClient.v2Project.list.query({
        organizationId: activeOrganizationId as string,
      }),
  });
+ const projectList = projects ?? [];
...
- const projectCount = projects.length;
+ const projectCount = projectList.length;
...
+ if (isError) {
+   return (
+     <StepShell backTo={STEP_ROUTES.permissions}>
+       <StepHeader
+         icon={supersetIcon}
+         title="Couldn't load projects"
+         subtitle="We hit an error while checking your organization projects."
+       />
+       <p className="select-text cursor-text text-[12px] text-red-400">
+         {error instanceof Error ? error.message : "Unknown error"}
+       </p>
+       <div className="flex w-[273px] flex-col gap-2 self-center">
+         <SetupButton variant="secondary" onClick={() => refetch()}>
+           Retry
+         </SetupButton>
+         <SetupButton variant="link" onClick={handleSkipStep}>
+           Skip for now
+         </SetupButton>
+       </div>
+     </StepShell>
+   );
+ }
🤖 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 `@apps/desktop/src/renderer/routes/_authenticated/setup/project/page.tsx`
around lines 33 - 40, The projects query currently only destructures data and
isLoading from useQuery, which hides failures; update the useQuery call that
calls apiTrpcClient.v2Project.list.query to also return isError, error, and
refetch (e.g., const { data: projects = [], isLoading, isError, error, refetch }
= useQuery(...)), then add an error state in the component that renders when
isError is true showing the error.message (or a fallback) and a retry control
that calls refetch; ensure the error text element includes the CSS classes
"select-text cursor-text" so the message is selectable.

@saddlepaddle saddlepaddle merged commit 0c6aeb6 into main May 7, 2026
10 checks passed
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 7, 2026

🧹 Preview Cleanup Complete

The following preview resources have been cleaned up:

  • ✅ Neon database branch

Thank you for your contribution! 🎉

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.

1 participant