From c5fb5fe2221914e8bf52eed9ee40ae846d4d1016 Mon Sep 17 00:00:00 2001 From: ndom91 Date: Wed, 20 Nov 2024 16:57:32 +0100 Subject: [PATCH] feat: add mergeAll btn --- apps/desktop/src/lib/pr/MergeButton.svelte | 42 +++++++++---- .../desktop/src/lib/pr/PullRequestCard.svelte | 3 +- apps/desktop/src/lib/stack/Stack.svelte | 61 ++++++++++++++++++- 3 files changed, 89 insertions(+), 17 deletions(-) diff --git a/apps/desktop/src/lib/pr/MergeButton.svelte b/apps/desktop/src/lib/pr/MergeButton.svelte index b8150cb481..6cbea2a5bd 100644 --- a/apps/desktop/src/lib/pr/MergeButton.svelte +++ b/apps/desktop/src/lib/pr/MergeButton.svelte @@ -4,20 +4,37 @@ import { MergeMethod } from '$lib/forge/interface/types'; import DropDownButton from '$lib/shared/DropDownButton.svelte'; import { persisted, type Persisted } from '@gitbutler/shared/persisted'; - import { createEventDispatcher } from 'svelte'; + import { type Props as ButtonProps } from '@gitbutler/ui/Button.svelte'; - export let projectId: string; - export let loading = false; - export let disabled = false; - export let wide = false; - export let tooltip = ''; + interface Props { + projectId: string; + onclick: (method: MergeMethod) => void; + loading?: boolean; + disabled?: boolean; + wide?: boolean; + tooltip?: string; + style?: ButtonProps['style']; + kind?: ButtonProps['kind']; + outline?: boolean; + } + + const { + projectId, + onclick, + loading = false, + disabled = false, + wide = false, + tooltip = '', + style = 'ghost', + kind = 'soft', + outline = true + }: Props = $props(); function persistedAction(projectId: string): Persisted { const key = 'projectMergeMethod'; return persisted(MergeMethod.Merge, key + projectId); } - const dispatch = createEventDispatcher<{ click: { method: MergeMethod } }>(); const action = persistedAction(projectId); let dropDown: ReturnType | undefined; @@ -30,16 +47,15 @@ onclick($action)} + {outline} + {style} + {kind} + {loading} {wide} {tooltip} {disabled} - onclick={() => { - dispatch('click', { method: $action }); - }} > {labels[$action]} {#snippet contextMenuSlot()} diff --git a/apps/desktop/src/lib/pr/PullRequestCard.svelte b/apps/desktop/src/lib/pr/PullRequestCard.svelte index 75c6249c00..54e52edfa7 100644 --- a/apps/desktop/src/lib/pr/PullRequestCard.svelte +++ b/apps/desktop/src/lib/pr/PullRequestCard.svelte @@ -285,10 +285,9 @@ disabled={mergability.disabled} tooltip={mergability.tooltip} loading={isMerging} - on:click={async (e) => { + onclick={async (method) => { if (!pr) return; isMerging = true; - const method = e.detail.method; try { await $prService?.merge(method, pr.number); await baseBranchService.fetchFromRemotes(); diff --git a/apps/desktop/src/lib/stack/Stack.svelte b/apps/desktop/src/lib/stack/Stack.svelte index 43d689048a..730a39cb35 100644 --- a/apps/desktop/src/lib/stack/Stack.svelte +++ b/apps/desktop/src/lib/stack/Stack.svelte @@ -5,16 +5,23 @@ import laneNewSvg from '$lib/assets/empty-state/lane-new.svg?raw'; import noChangesSvg from '$lib/assets/empty-state/lane-no-changes.svg?raw'; import { Project } from '$lib/backend/projects'; + import { BaseBranchService } from '$lib/baseBranch/baseBranchService'; import Dropzones from '$lib/branch/Dropzones.svelte'; import { getForgeListingService } from '$lib/forge/interface/forgeListingService'; + import { getForgePrService } from '$lib/forge/interface/forgePrService'; + import { type MergeMethod } from '$lib/forge/interface/types'; + import { showError } from '$lib/notifications/toasts'; + import MergeButton from '$lib/pr/MergeButton.svelte'; import ScrollableContainer from '$lib/scroll/ScrollableContainer.svelte'; import { SETTINGS, type Settings } from '$lib/settings/userSettings'; import Resizer from '$lib/shared/Resizer.svelte'; import CollapsedLane from '$lib/stack/CollapsedLane.svelte'; import { intersectionObserver } from '$lib/utils/intersectionObserver'; + import * as toasts from '$lib/utils/toasts'; import { BranchController } from '$lib/vbranches/branchController'; import { FileIdSelection } from '$lib/vbranches/fileIdSelection'; import { DetailedCommit, VirtualBranch } from '$lib/vbranches/types'; + import { VirtualBranchService } from '$lib/vbranches/virtualBranch'; import { getContext, getContextStore, getContextStoreBySymbol } from '@gitbutler/shared/context'; import { persisted } from '@gitbutler/shared/persisted'; import Button from '@gitbutler/ui/Button.svelte'; @@ -29,20 +36,29 @@ commitBoxOpen }: { isLaneCollapsed: Writable; commitBoxOpen: Writable } = $props(); + const vbranchService = getContext(VirtualBranchService); const branchController = getContext(BranchController); const fileIdSelection = getContext(FileIdSelection); const branchStore = getContextStore(VirtualBranch); + const baseBranchService = getContext(BaseBranchService); const project = getContext(Project); + const prService = getForgePrService(); const branch = $derived($branchStore); const userSettings = getContextStoreBySymbol(SETTINGS); const defaultBranchWidthRem = persisted(24, 'defaulBranchWidth' + project.id); - const laneWidthKey = 'laneWidth_'; let lastPush = $state(); + let canMergeAll = $derived.by(() => { + console.log('canMergeAll.validSeries', branch.validSeries); + const validSeries = branch.validSeries.filter((s) => !s.archived).length; + const validSeriesWithPrs = branch.validSeries.filter((s) => s.prNumber && !s.archived).length; + return validSeries === validSeriesWithPrs; + }); + const laneWidthKey = 'laneWidth_'; let laneWidth: number | undefined = $state(); - let rsViewport = $state(); + const branchHasFiles = $derived(branch.files !== undefined && branch.files.length > 0); const branchHasNoCommits = $derived(branch.commits !== undefined && branch.commits.length === 0); @@ -58,6 +74,7 @@ let scrollEndVisible = $state(true); let isPushingCommits = $state(false); + let isMergingSeries = $state(false); const { upstreamPatches, branchPatches, hasConflicts } = $derived.by(() => { let hasConflicts = false; @@ -96,6 +113,30 @@ isPushingCommits = false; } } + + async function mergeAll(method: MergeMethod) { + isMergingSeries = true; + try { + for (const validBranch of branch.validSeries.reverse()) { + console.log('validBranch', validBranch); + if (validBranch.prNumber && $prService) { + await $prService.merge(method, validBranch.prNumber); + toasts.success(`Merged PR ${validBranch.prNumber}`); + await Promise.all([ + $prService?.prMonitor(validBranch.prNumber).refresh(), + $listingService?.refresh(), + vbranchService.refresh(), + baseBranchService.refresh() + ]); + } + } + } catch (e) { + console.error(e); + showError('Failed to merge PR', e); + } finally { + isMergingSeries = false; + } + } {#if $isLaneCollapsed} @@ -161,6 +202,7 @@
{ if (entry?.isIntersecting) { @@ -193,6 +235,17 @@ ? 'Push All' : 'Push'} + {#if canMergeAll} + + {/if}
{/if} @@ -245,6 +298,10 @@ bottom: 0; transition: background-color var(--transition-fast); + &:global(.can-merge-all > button:not(:last-child)) { + margin-bottom: 8px; + } + &:after { content: ''; display: block;