22 commitCreateMutationOptions ,
33 commitInsertBlankMutationOptions ,
44 commitRewordMutationOptions ,
5+ removeBranchMutationOptions ,
56 updateBranchNameMutationOptions ,
67 unapplyStackMutationOptions ,
78} from "#ui/api/mutations.ts" ;
@@ -65,7 +66,15 @@ import {
6566 attachInstruction ,
6667 extractInstruction ,
6768} from "@atlaskit/pragmatic-drag-and-drop-hitbox/list-item" ;
68- import { ContextMenu , Menu , mergeProps , Tooltip , Toast , useRender } from "@base-ui/react" ;
69+ import {
70+ AlertDialog ,
71+ ContextMenu ,
72+ Menu ,
73+ mergeProps ,
74+ Tooltip ,
75+ Toast ,
76+ useRender ,
77+ } from "@base-ui/react" ;
6978import {
7079 Commit ,
7180 DiffHunk ,
@@ -122,6 +131,7 @@ import {
122131import { PositionedShortcutsBar } from "../-ShortcutsBar.tsx" ;
123132import { formatShortcutKeys , ShortcutActionBase , type ShortcutBinding } from "#ui/shortcuts.ts" ;
124133import styles from "./route.module.css" ;
134+ import { RemoveBranchParams } from "#electron/ipc.ts" ;
125135
126136// https://linear.app/gitbutler/issue/GB-1161/refsbranches-should-use-bytes-instead-of-strings
127137const decodeRefName = ( fullNameBytes : Array < number > ) : string =>
@@ -1428,21 +1438,58 @@ const TearOffBranchTarget: FC<useRender.ComponentProps<"div">> = ({ render, ...p
14281438} ;
14291439
14301440const SegmentMenuPopup : FC < {
1431- canRename : boolean ;
1432- onRename : ( ) => void ;
1441+ canRemoveBranch : boolean ;
1442+ canRenameBranch : boolean ;
1443+ onRemoveBranch : ( ) => void ;
1444+ onRenameBranch : ( ) => void ;
14331445 parts : typeof Menu | typeof ContextMenu ;
1434- } > = ( { canRename , onRename , parts } ) => {
1446+ } > = ( { canRemoveBranch , canRenameBranch , onRemoveBranch , onRenameBranch , parts } ) => {
14351447 const { Popup, Item } = parts ;
14361448
14371449 return (
14381450 < Popup className = { classes ( uiStyles . popup , uiStyles . menuPopup ) } >
1439- < Item className = { uiStyles . menuItem } disabled = { ! canRename } onClick = { onRename } >
1451+ < Item className = { uiStyles . menuItem } disabled = { ! canRenameBranch } onClick = { onRenameBranch } >
14401452 Rename branch
14411453 </ Item >
1454+ < Item className = { uiStyles . menuItem } disabled = { ! canRemoveBranch } onClick = { onRemoveBranch } >
1455+ Remove branch
1456+ </ Item >
14421457 </ Popup >
14431458 ) ;
14441459} ;
14451460
1461+ const RemoveBranchDialog : FC < {
1462+ branchName : string ;
1463+ isPending : boolean ;
1464+ onConfirm : ( ) => void ;
1465+ onOpenChange : ( open : boolean ) => void ;
1466+ } > = ( { branchName, isPending, onConfirm, onOpenChange } ) => (
1467+ < AlertDialog . Root open onOpenChange = { onOpenChange } >
1468+ < AlertDialog . Portal >
1469+ < AlertDialog . Backdrop className = { uiStyles . dialogBackdrop } />
1470+ < AlertDialog . Popup className = { uiStyles . dialogPopup } >
1471+ < AlertDialog . Title > Remove branch</ AlertDialog . Title >
1472+ < p >
1473+ Are you sure you want to remove < code > { branchName } </ code > ?
1474+ </ p >
1475+ < div className = { styles . dialogActions } >
1476+ < AlertDialog . Close className = { uiStyles . button } disabled = { isPending } >
1477+ Cancel
1478+ </ AlertDialog . Close >
1479+ < button
1480+ type = "button"
1481+ className = { uiStyles . button }
1482+ onClick = { onConfirm }
1483+ disabled = { isPending }
1484+ >
1485+ Remove branch
1486+ </ button >
1487+ </ div >
1488+ </ AlertDialog . Popup >
1489+ </ AlertDialog . Portal >
1490+ </ AlertDialog . Root >
1491+ ) ;
1492+
14461493const InlineBranchNameEditor : FC < {
14471494 branchName : string ;
14481495 onSubmit : ( value : string ) => void ;
@@ -1484,6 +1531,7 @@ const SegmentRow: FC<
14841531 {
14851532 projectId : string ;
14861533 editing : Editing | null ;
1534+ onRequestRemoveBranch : ( params : { stackId : string ; branchName : string } ) => void ;
14871535 segment : Segment ;
14881536 stackId : string ;
14891537 segmentIndex : number ;
@@ -1494,6 +1542,7 @@ const SegmentRow: FC<
14941542> = ( {
14951543 projectId,
14961544 editing,
1545+ onRequestRemoveBranch,
14971546 segment,
14981547 stackId,
14991548 segmentIndex,
@@ -1560,6 +1609,11 @@ const SegmentRow: FC<
15601609 } ) ;
15611610 } ;
15621611
1612+ const requestRemoveBranch = ( ) => {
1613+ if ( branchName === null ) return ;
1614+ onRequestRemoveBranch ( { stackId, branchName } ) ;
1615+ } ;
1616+
15631617 const children = (
15641618 < div
15651619 { ...restProps }
@@ -1594,8 +1648,10 @@ const SegmentRow: FC<
15941648 < ContextMenu . Portal >
15951649 < ContextMenu . Positioner >
15961650 < SegmentMenuPopup
1597- canRename = { branchName !== null && ! isRenamePending }
1598- onRename = { startEditing }
1651+ canRemoveBranch = { branchName !== null }
1652+ canRenameBranch = { branchName !== null && ! isRenamePending }
1653+ onRemoveBranch = { requestRemoveBranch }
1654+ onRenameBranch = { startEditing }
15991655 parts = { ContextMenu }
16001656 />
16011657 </ ContextMenu . Positioner >
@@ -1612,8 +1668,10 @@ const SegmentRow: FC<
16121668 < Menu . Portal >
16131669 < Menu . Positioner align = "end" >
16141670 < SegmentMenuPopup
1615- canRename = { branchName !== null && ! isRenamePending }
1616- onRename = { startEditing }
1671+ canRemoveBranch = { branchName !== null }
1672+ canRenameBranch = { branchName !== null && ! isRenamePending }
1673+ onRemoveBranch = { requestRemoveBranch }
1674+ onRenameBranch = { startEditing }
16171675 parts = { Menu }
16181676 />
16191677 </ Menu . Positioner >
@@ -1641,6 +1699,7 @@ const SegmentRow: FC<
16411699
16421700const SegmentC : FC < {
16431701 highlightedCommitIds : Set < string > ;
1702+ onRequestRemoveBranch : ( params : { stackId : string ; branchName : string } ) => void ;
16441703 projectId : string ;
16451704 segment : Segment ;
16461705 segmentIndex : number ;
@@ -1652,6 +1711,7 @@ const SegmentC: FC<{
16521711} > = ( {
16531712 editing,
16541713 highlightedCommitIds,
1714+ onRequestRemoveBranch,
16551715 projectId,
16561716 segment,
16571717 segmentIndex,
@@ -1673,6 +1733,7 @@ const SegmentC: FC<{
16731733 < SegmentRow
16741734 projectId = { projectId }
16751735 editing = { editing }
1736+ onRequestRemoveBranch = { onRequestRemoveBranch }
16761737 segment = { segment }
16771738 stackId = { stackId }
16781739 segmentIndex = { segmentIndex }
@@ -1708,6 +1769,7 @@ const StackC: FC<{
17081769 highlightedCommitIds : Set < string > ;
17091770 onAbsorbChanges : ( changes : Array < TreeChange > , stackId : string | null ) => void ;
17101771 onDependencyHover : ( commitIds : Array < string > | null ) => void ;
1772+ onRequestRemoveBranch : ( params : Omit < RemoveBranchParams , "projectId" > ) => void ;
17111773 projectId : string ;
17121774 selection : Item | null ;
17131775 select : ( selection : Item | null ) => void ;
@@ -1719,6 +1781,7 @@ const StackC: FC<{
17191781 highlightedCommitIds,
17201782 onAbsorbChanges,
17211783 onDependencyHover,
1784+ onRequestRemoveBranch,
17221785 projectId,
17231786 selection,
17241787 select,
@@ -1770,6 +1833,7 @@ const StackC: FC<{
17701833 < SegmentC
17711834 editing = { editing }
17721835 highlightedCommitIds = { highlightedCommitIds }
1836+ onRequestRemoveBranch = { onRequestRemoveBranch }
17731837 projectId = { projectId }
17741838 segment = { segment }
17751839 segmentIndex = { segmentIndex }
@@ -1832,6 +1896,28 @@ const ProjectPage: FC = () => {
18321896 } = useAbsorption ( projectId ) ;
18331897
18341898 useMonitorDraggedSourceItem ( { projectId } ) ;
1899+
1900+ const removeBranch = useMutation ( removeBranchMutationOptions ) ;
1901+ const [ branchPendingRemoval , setBranchPendingRemoval ] = useState < Omit <
1902+ RemoveBranchParams ,
1903+ "projectId"
1904+ > | null > ( null ) ;
1905+ const confirmRemoveBranch = ( ) => {
1906+ if ( branchPendingRemoval === null ) return ;
1907+
1908+ removeBranch . mutate (
1909+ {
1910+ projectId,
1911+ stackId : branchPendingRemoval . stackId ,
1912+ branchName : branchPendingRemoval . branchName ,
1913+ } ,
1914+ {
1915+ onSuccess : ( ) => {
1916+ setBranchPendingRemoval ( null ) ;
1917+ } ,
1918+ } ,
1919+ ) ;
1920+ } ;
18351921 useWorkspaceShortcuts ( {
18361922 projectId,
18371923 showFullscreenPreview,
@@ -1841,6 +1927,10 @@ const ProjectPage: FC = () => {
18411927 setEditing,
18421928 commonBaseCommitId,
18431929 onAbsorbChanges : requestAbsorptionPlan ,
1930+ onRemoveBranch : ( { stackId, branchName } ) => {
1931+ if ( branchName === null ) return ;
1932+ setBranchPendingRemoval ( { stackId, branchName } ) ;
1933+ } ,
18441934 } ) ;
18451935
18461936 // TODO: dedupe
@@ -1884,6 +1974,7 @@ const ProjectPage: FC = () => {
18841974 highlightedCommitIds = { highlightedCommitIds }
18851975 onAbsorbChanges = { requestAbsorptionPlan }
18861976 onDependencyHover = { highlightCommits }
1977+ onRequestRemoveBranch = { setBranchPendingRemoval }
18871978 projectId = { project . id }
18881979 selection = { selection }
18891980 select = { select }
@@ -1926,6 +2017,7 @@ const ProjectPage: FC = () => {
19262017 label = { shortcutScope ? getLabel ( shortcutScope ) : null }
19272018 items = { shortcutScope ?. bindings ?? [ ] }
19282019 />
2020+
19292021 { absorptionPlan !== null && (
19302022 < AbsorptionDialog
19312023 absorptionPlan = { absorptionPlan }
@@ -1936,6 +2028,16 @@ const ProjectPage: FC = () => {
19362028 } }
19372029 />
19382030 ) }
2031+ { branchPendingRemoval !== null && (
2032+ < RemoveBranchDialog
2033+ branchName = { branchPendingRemoval . branchName }
2034+ isPending = { removeBranch . isPending }
2035+ onConfirm = { confirmRemoveBranch }
2036+ onOpenChange = { ( open ) => {
2037+ if ( ! open ) setBranchPendingRemoval ( null ) ;
2038+ } }
2039+ />
2040+ ) }
19392041 </ ProjectPreviewLayout >
19402042 ) ;
19412043} ;
0 commit comments