-
Notifications
You must be signed in to change notification settings - Fork 963
feat(desktop): rename branch from workspace hover card #3793
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,140 @@ | ||
| import { Button } from "@superset/ui/button"; | ||
| import { | ||
| Dialog, | ||
| DialogContent, | ||
| DialogDescription, | ||
| DialogFooter, | ||
| DialogHeader, | ||
| DialogTitle, | ||
| } from "@superset/ui/dialog"; | ||
| import { Input } from "@superset/ui/input"; | ||
| import { Label } from "@superset/ui/label"; | ||
| import { toast } from "@superset/ui/sonner"; | ||
| import { useEffect, useState } from "react"; | ||
| import { electronTrpc } from "renderer/lib/electron-trpc"; | ||
| import { getHostServiceClientByUrl } from "renderer/lib/host-service-client"; | ||
| import { useLocalHostService } from "renderer/routes/_authenticated/providers/LocalHostServiceProvider"; | ||
|
|
||
| interface RenameBranchDialogProps { | ||
| workspaceId: string; | ||
| currentBranchName: string; | ||
| open: boolean; | ||
| onOpenChange: (open: boolean) => void; | ||
| onAfterRename?: (newName: string) => void; | ||
| } | ||
|
|
||
| export function RenameBranchDialog({ | ||
| workspaceId, | ||
| currentBranchName, | ||
| open, | ||
| onOpenChange, | ||
| onAfterRename, | ||
| }: RenameBranchDialogProps) { | ||
| const [value, setValue] = useState(currentBranchName); | ||
| const [isSubmitting, setIsSubmitting] = useState(false); | ||
| const electronUtils = electronTrpc.useUtils(); | ||
| const { activeHostUrl } = useLocalHostService(); | ||
|
|
||
| useEffect(() => { | ||
| if (open) setValue(currentBranchName); | ||
| }, [open, currentBranchName]); | ||
|
|
||
| const trimmed = value.trim(); | ||
| const isUnchanged = trimmed === currentBranchName; | ||
| const isInvalid = trimmed.length === 0 || isUnchanged; | ||
|
|
||
| const handleSubmit = async () => { | ||
| if (isInvalid || isSubmitting) return; | ||
| if (!activeHostUrl) { | ||
| toast.error("Host service is not available"); | ||
| return; | ||
| } | ||
|
|
||
| const client = getHostServiceClientByUrl(activeHostUrl); | ||
| const renamePromise = client.git.renameBranch.mutate({ | ||
| workspaceId, | ||
| oldName: currentBranchName, | ||
| newName: trimmed, | ||
| }); | ||
|
|
||
| toast.promise(renamePromise, { | ||
| loading: `Renaming branch to ${trimmed}...`, | ||
| success: `Branch renamed to ${trimmed}`, | ||
| error: (err) => | ||
| err instanceof Error ? err.message : "Failed to rename branch", | ||
| }); | ||
|
|
||
| setIsSubmitting(true); | ||
| try { | ||
|
Comment on lines
+46
to
+68
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Prompt To Fix With AIThis is a comment left during a code review.
Path: apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/components/RenameBranchDialog/RenameBranchDialog.tsx
Line: 46-68
Comment:
**`setIsSubmitting` called after async operation begins**
`setIsSubmitting(true)` is set after `toast.promise(renamePromise, ...)` starts and the RPC call is already in-flight. Because `isSubmitting` is React state (not a ref), if `handleSubmit` is invoked twice before the first re-render (e.g. Enter key + button click in the same tick), both calls pass the `if (isSubmitting) return` guard and two concurrent rename mutations fire. Moving `setIsSubmitting(true)` above the `client.git.renameBranch.mutate` call would close the window.
How can I resolve this? If you propose a fix, please make it concise. |
||
| await renamePromise; | ||
| onAfterRename?.(trimmed); | ||
| void electronUtils.workspaces.getWorktreeInfo.invalidate({ | ||
| workspaceId, | ||
| }); | ||
| void electronUtils.workspaces.get.invalidate({ id: workspaceId }); | ||
| void electronUtils.workspaces.getAllGrouped.invalidate(); | ||
| onOpenChange(false); | ||
| } catch { | ||
| // toast.promise surfaced the error to the user | ||
| } finally { | ||
| setIsSubmitting(false); | ||
| } | ||
| }; | ||
|
|
||
| return ( | ||
| <Dialog open={open} onOpenChange={onOpenChange} modal> | ||
| <DialogContent className="max-w-[420px]"> | ||
| <DialogHeader> | ||
| <DialogTitle>Rename branch</DialogTitle> | ||
| <DialogDescription> | ||
| Rename the local branch. Branches that have been pushed to remote | ||
| cannot be renamed. | ||
| </DialogDescription> | ||
| </DialogHeader> | ||
| <form | ||
| onSubmit={(e) => { | ||
| e.preventDefault(); | ||
| void handleSubmit(); | ||
| }} | ||
| className="space-y-4" | ||
| > | ||
| <div className="space-y-1.5"> | ||
| <Label htmlFor="rename-branch-input" className="text-xs"> | ||
| Branch name | ||
| </Label> | ||
| <Input | ||
| id="rename-branch-input" | ||
| value={value} | ||
| onChange={(e) => setValue(e.target.value)} | ||
| onKeyDown={(e) => { | ||
| if (e.key === "Enter") { | ||
| e.preventDefault(); | ||
| e.stopPropagation(); | ||
| void handleSubmit(); | ||
| } | ||
| }} | ||
| autoFocus | ||
| disabled={isSubmitting} | ||
| spellCheck={false} | ||
| autoComplete="off" | ||
| className="font-mono" | ||
| /> | ||
| </div> | ||
| <DialogFooter> | ||
| <Button | ||
| type="button" | ||
| variant="ghost" | ||
| onClick={() => onOpenChange(false)} | ||
| disabled={isSubmitting} | ||
| > | ||
| Cancel | ||
| </Button> | ||
| <Button type="submit" disabled={isInvalid || isSubmitting}> | ||
| Rename | ||
| </Button> | ||
| </DialogFooter> | ||
| </form> | ||
| </DialogContent> | ||
| </Dialog> | ||
| ); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| export { RenameBranchDialog } from "./RenameBranchDialog"; |
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.
openprop inside conditional renderrenameBranchTarget !== nullis alwaystrueinside the{renameBranchTarget && ...}guard, soopenis a constanttruehere. The same pattern appears in the second return path (line ~253) and inCollapsedWorkspaceItem.tsx/WorkspaceContextMenu.tsx. Consider either always rendering the dialog and driving visibility purely throughopen={!!renameBranchTarget}, or simplifying toopen={true}inside the guard.Prompt To Fix With AI