Repository navigation
fix(desktop): drop onboarding auto-skip, align SetupButton with design system #4125
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
1111995
fix(desktop): drop onboarding auto-skip, switch SetupButton to design…
saddlepaddle def431e
fix(desktop): create v2 projects from onboarding when v2 is enabled
saddlepaddle f23aa74
refactor(desktop): make onboarding project step v2-only
saddlepaddle d5d0c5d
fix(desktop): keep onboarding flow after import in project step
saddlepaddle 85125db
feat(desktop): make onboarding v2-compatible end to end
saddlepaddle File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| export { | ||
| type EnsureV2ProjectResult, | ||
| useEnsureV2Project, | ||
| } from "./useEnsureV2Project"; |
66 changes: 66 additions & 0 deletions
66
apps/desktop/src/renderer/hooks/useEnsureV2Project/useEnsureV2Project.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| import { useCallback } from "react"; | ||
| import { getHostServiceClientByUrl } from "renderer/lib/host-service-client"; | ||
| import { useLocalHostService } from "renderer/routes/_authenticated/providers/LocalHostServiceProvider"; | ||
|
|
||
| export interface EnsureV2ProjectResult { | ||
| hostUrl: string; | ||
| projectId: string; | ||
| repoPath: string; | ||
| mainWorkspaceId: string | null; | ||
| } | ||
|
|
||
| export function useEnsureV2Project(): (args: { | ||
| repoPath: string; | ||
| name: string; | ||
| }) => Promise<EnsureV2ProjectResult> { | ||
| const { activeHostUrl } = useLocalHostService(); | ||
|
|
||
| return useCallback( | ||
| async ({ repoPath, name }) => { | ||
| if (!activeHostUrl) { | ||
| throw new Error("No active host service"); | ||
| } | ||
| const hostService = getHostServiceClientByUrl(activeHostUrl); | ||
|
|
||
| const found = await hostService.project.findByPath.query({ repoPath }); | ||
| const candidate = found.candidates[0]; | ||
| if (candidate) { | ||
| try { | ||
| const setupResult = await hostService.project.setup.mutate({ | ||
| projectId: candidate.id, | ||
| mode: { kind: "import", repoPath }, | ||
| }); | ||
| return { | ||
| hostUrl: activeHostUrl, | ||
| projectId: candidate.id, | ||
| repoPath: setupResult.repoPath, | ||
| mainWorkspaceId: setupResult.mainWorkspaceId, | ||
| }; | ||
| } catch (err) { | ||
| // findByPath returns local sqlite rows even when no cloud v2 project | ||
| // exists for that id; setup → v2Project.get → NOT_FOUND. Only that | ||
| // case is safe to fall through to create — every other error | ||
| // (network, auth, 5xx) must propagate so retries don't silently mint | ||
| // duplicate cloud projects. | ||
| const code = (err as { data?: { code?: string } } | null | undefined) | ||
| ?.data?.code; | ||
| if (code !== "NOT_FOUND") { | ||
| throw err; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| const created = await hostService.project.create.mutate({ | ||
| name, | ||
| mode: { kind: "importLocal", repoPath }, | ||
| }); | ||
| return { | ||
| hostUrl: activeHostUrl, | ||
| projectId: created.projectId, | ||
| repoPath: created.repoPath, | ||
| mainWorkspaceId: created.mainWorkspaceId, | ||
| }; | ||
| }, | ||
| [activeHostUrl], | ||
| ); | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Multiple
findByPathcandidates are silently ignored — risks linking to the wrong project.findByPathreturns acandidateslist, and the siblinguseFolderFirstImportexplicitly treatscandidates.length > 1as an error case (surfacing it to the caller viaonMultipleProjects/onError) precisely because more than one local sqlite row can match the samerepoPath. Here, the hook just pickscandidates[0]and proceeds tosetup.mutateagainst it.When this hook is used during adopt-worktrees, this means a project whose
mainRepoPathmatches multiple historical entries will be linked to whichever one happens to come first — potentially the wrong cloud project — without any error or user prompt.Consider mirroring
useFolderFirstImport's behavior: iffound.candidates.length > 1, throw (or surface to the caller) so the caller can decide rather than silently disambiguating.🛡️ Suggested guard
const found = await hostService.project.findByPath.query({ repoPath }); -const candidate = found.candidates[0]; -if (candidate) { +if (found.candidates.length > 1) { + throw new Error( + `Multiple projects use this repository (${found.candidates.length}). Open the intended one from settings before importing.`, + ); +} +const candidate = found.candidates[0]; +if (candidate) { try {🤖 Prompt for AI Agents