mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-12-28 20:15:20 +03:00
Stack Headers: Use new PR button
The new PR headers use the new PR details modal
This commit is contained in:
parent
5cc83a4fdb
commit
718fa8d2ff
@ -2,26 +2,19 @@
|
||||
import BranchLabel from './BranchLabel.svelte';
|
||||
import StackingStatusIcon from './StackingStatusIcon.svelte';
|
||||
import { getColorFromBranchType } from './stackingUtils';
|
||||
import { Project } from '$lib/backend/projects';
|
||||
import { BaseBranch } from '$lib/baseBranch/baseBranch';
|
||||
import { BaseBranchService } from '$lib/baseBranch/baseBranchService';
|
||||
import StackingBranchHeaderContextMenu from '$lib/branch/StackingBranchHeaderContextMenu.svelte';
|
||||
import ContextMenu from '$lib/components/contextmenu/ContextMenu.svelte';
|
||||
import { mapErrorToToast } from '$lib/gitHost/github/errorMap';
|
||||
import { getGitHost } from '$lib/gitHost/interface/gitHost';
|
||||
import { getGitHostListingService } from '$lib/gitHost/interface/gitHostListingService';
|
||||
import { getGitHostPrService } from '$lib/gitHost/interface/gitHostPrService';
|
||||
import { showError, showToast } from '$lib/notifications/toasts';
|
||||
import PullRequestButton from '$lib/pr/PullRequestButton.svelte';
|
||||
import PrDetailsModal from '$lib/pr/PrDetailsModal.svelte';
|
||||
import StackingPullRequestCard from '$lib/pr/StackingPullRequestCard.svelte';
|
||||
import { getContext, getContextStore } from '$lib/utils/context';
|
||||
import { sleep } from '$lib/utils/sleep';
|
||||
import { error } from '$lib/utils/toasts';
|
||||
import { openExternalUrl } from '$lib/utils/url';
|
||||
import { BranchController } from '$lib/vbranches/branchController';
|
||||
import { DetailedCommit, VirtualBranch, type CommitStatus } from '$lib/vbranches/types';
|
||||
import Button from '@gitbutler/ui/Button.svelte';
|
||||
import type { PullRequest } from '$lib/gitHost/interface/types';
|
||||
|
||||
interface Props {
|
||||
name: string;
|
||||
@ -31,7 +24,6 @@
|
||||
|
||||
const { name, upstreamName, commits }: Props = $props();
|
||||
|
||||
let isLoading = $state(false);
|
||||
let descriptionVisible = $state(false);
|
||||
|
||||
const branchStore = getContextStore(VirtualBranch);
|
||||
@ -39,16 +31,12 @@
|
||||
|
||||
const branchController = getContext(BranchController);
|
||||
const baseBranch = getContextStore(BaseBranch);
|
||||
const baseBranchService = getContext(BaseBranchService);
|
||||
const prService = getGitHostPrService();
|
||||
const gitListService = getGitHostListingService();
|
||||
const gitHost = getGitHost();
|
||||
const gitHostBranch = $derived(upstreamName ? $gitHost?.branch(upstreamName) : undefined);
|
||||
const project = getContext(Project);
|
||||
|
||||
const baseBranchName = $derived($baseBranch.shortName);
|
||||
|
||||
let contextMenu = $state<ReturnType<typeof ContextMenu>>();
|
||||
let prDetailsModal = $state<ReturnType<typeof PrDetailsModal>>();
|
||||
let meatballButtonEl = $state<HTMLDivElement>();
|
||||
|
||||
const branchColorType = $derived<CommitStatus>(branch.commits?.[0]?.status ?? 'local');
|
||||
@ -66,95 +54,8 @@
|
||||
const prMonitor = $derived(prNumber ? $prService?.prMonitor(prNumber) : undefined);
|
||||
const pr = $derived(prMonitor?.pr);
|
||||
|
||||
interface CreatePrOpts {
|
||||
draft: boolean;
|
||||
}
|
||||
|
||||
const defaultPrOpts: CreatePrOpts = {
|
||||
draft: true
|
||||
};
|
||||
|
||||
async function createPr(createPrOpts: CreatePrOpts): Promise<PullRequest | undefined> {
|
||||
const opts = { ...defaultPrOpts, ...createPrOpts };
|
||||
if (!$gitHost) {
|
||||
error('Pull request service not available');
|
||||
return;
|
||||
}
|
||||
|
||||
let title: string;
|
||||
let body: string;
|
||||
|
||||
let pullRequestTemplateBody: string | undefined;
|
||||
const prTemplatePath = project.git_host.pullRequestTemplatePath;
|
||||
|
||||
if (prTemplatePath) {
|
||||
pullRequestTemplateBody = await $prService?.pullRequestTemplateContent(
|
||||
prTemplatePath,
|
||||
project.id
|
||||
);
|
||||
}
|
||||
|
||||
if (pullRequestTemplateBody) {
|
||||
title = name;
|
||||
body = pullRequestTemplateBody;
|
||||
} else {
|
||||
// In case of a single commit, use the commit summary and description for the title and
|
||||
// description of the PR.
|
||||
if (commits.length === 1) {
|
||||
const commit = commits[0];
|
||||
title = commit?.descriptionTitle ?? '';
|
||||
body = commit?.descriptionBody ?? '';
|
||||
} else {
|
||||
title = name;
|
||||
body = '';
|
||||
}
|
||||
}
|
||||
|
||||
isLoading = true;
|
||||
try {
|
||||
let upstreamBranchName: string | undefined = upstreamName;
|
||||
|
||||
if (commits.some((c) => !c.isRemote)) {
|
||||
const firstPush = !branch.upstream;
|
||||
await branchController.pushBranch(branch.id, branch.requiresForce, true);
|
||||
if (firstPush) {
|
||||
// TODO: fix this hack for reactively available prService.
|
||||
await sleep(500);
|
||||
}
|
||||
}
|
||||
|
||||
if (!baseBranchName) {
|
||||
error('No base branch name determined');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!upstreamBranchName) {
|
||||
error('No upstream branch name determined');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$prService) {
|
||||
error('Pull request service not available');
|
||||
return;
|
||||
}
|
||||
|
||||
await $prService.createPr({
|
||||
title,
|
||||
body,
|
||||
draft: opts.draft,
|
||||
baseBranchName,
|
||||
upstreamName: upstreamBranchName
|
||||
});
|
||||
} catch (err: any) {
|
||||
console.error(err);
|
||||
const toast = mapErrorToToast(err);
|
||||
if (toast) showToast(toast);
|
||||
else showError('Error while creating pull request', err);
|
||||
} finally {
|
||||
isLoading = false;
|
||||
}
|
||||
await $gitListService?.refresh();
|
||||
baseBranchService.fetchFromRemotes();
|
||||
function handleOpenPR() {
|
||||
prDetailsModal?.show();
|
||||
}
|
||||
|
||||
function editTitle(title: string) {
|
||||
@ -221,19 +122,19 @@
|
||||
{#if $pr}
|
||||
<StackingPullRequestCard pr={$pr} {prMonitor} sourceBranch={$pr.sourceBranch} />
|
||||
{:else}
|
||||
<PullRequestButton
|
||||
click={async ({ draft }) => await createPr({ draft })}
|
||||
<Button
|
||||
style="ghost"
|
||||
outline
|
||||
disabled={commits.length === 0 || !$gitHost || !$prService}
|
||||
tooltip={!$gitHost || !$prService
|
||||
? 'You can enable git host integration in the settings'
|
||||
: ''}
|
||||
loading={isLoading}
|
||||
/>
|
||||
onclick={handleOpenPR}>Start a PR</Button
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<PrDetailsModal bind:this={prDetailsModal} type="preview-series" {upstreamName} {name} {commits} />
|
||||
|
||||
<style lang="postcss">
|
||||
.branch-header {
|
||||
display: flex;
|
||||
|
@ -30,7 +30,7 @@
|
||||
import { sleep } from '$lib/utils/sleep';
|
||||
import { error } from '$lib/utils/toasts';
|
||||
import { BranchController } from '$lib/vbranches/branchController';
|
||||
import { VirtualBranch } from '$lib/vbranches/types';
|
||||
import { DetailedCommit, VirtualBranch } from '$lib/vbranches/types';
|
||||
import Button from '@gitbutler/ui/Button.svelte';
|
||||
import Checkbox from '@gitbutler/ui/Checkbox.svelte';
|
||||
import Modal from '@gitbutler/ui/Modal.svelte';
|
||||
@ -38,7 +38,7 @@
|
||||
import type { DetailedPullRequest, PullRequest } from '$lib/gitHost/interface/types';
|
||||
|
||||
interface BaseProps {
|
||||
type: 'display' | 'preview';
|
||||
type: 'display' | 'preview' | 'preview-series';
|
||||
}
|
||||
|
||||
interface DisplayProps extends BaseProps {
|
||||
@ -50,7 +50,14 @@
|
||||
type: 'preview';
|
||||
}
|
||||
|
||||
type Props = DisplayProps | PreviewProps;
|
||||
interface PreviewSeriesProps {
|
||||
type: 'preview-series';
|
||||
name: string;
|
||||
upstreamName?: string;
|
||||
commits: DetailedCommit[];
|
||||
}
|
||||
|
||||
type Props = DisplayProps | PreviewProps | PreviewSeriesProps;
|
||||
|
||||
let props: Props = $props();
|
||||
|
||||
@ -68,6 +75,11 @@
|
||||
const preferredPRAction = getPreferredPRAction();
|
||||
|
||||
const branch = $derived($branchStore);
|
||||
const branchName = $derived(props.type === 'preview-series' ? props.name : branch.name);
|
||||
const commits = $derived(props.type === 'preview-series' ? props.commits : branch.commits);
|
||||
const upstreamName = $derived(
|
||||
props.type === 'preview-series' ? props.upstreamName : branch.upstreamName
|
||||
);
|
||||
const baseBranchName = $derived($baseBranch.shortName);
|
||||
const prTemplatePath = $derived(project.git_host.pullRequestTemplatePath);
|
||||
const isDraft = $derived<boolean>($preferredPRAction === PRAction.CreateDraft);
|
||||
@ -88,11 +100,11 @@
|
||||
const defaultTitle: string = $derived.by(() => {
|
||||
if (props.type === 'display') return props.pr.title;
|
||||
// In case of a single commit, use the commit summary for the title
|
||||
if (branch.commits.length === 1) {
|
||||
const commit = branch.commits[0];
|
||||
if (commits.length === 1) {
|
||||
const commit = commits[0];
|
||||
return commit?.descriptionTitle ?? '';
|
||||
} else {
|
||||
return branch.name;
|
||||
return branchName;
|
||||
}
|
||||
});
|
||||
|
||||
@ -100,8 +112,8 @@
|
||||
if (props.type === 'display') return props.pr.body ?? '';
|
||||
if (pullRequestTemplateBody) return pullRequestTemplateBody;
|
||||
// In case of a single commit, use the commit description for the body
|
||||
if (branch.commits.length === 1) {
|
||||
const commit = branch.commits[0];
|
||||
if (commits.length === 1) {
|
||||
const commit = commits[0];
|
||||
return commit?.descriptionBody ?? '';
|
||||
} else {
|
||||
return '';
|
||||
@ -142,15 +154,19 @@
|
||||
|
||||
isLoading = true;
|
||||
try {
|
||||
let upstreamBranchName = branch.upstreamName;
|
||||
let upstreamBranchName = upstreamName;
|
||||
|
||||
if (branch.commits.some((c) => !c.isRemote)) {
|
||||
if (commits.some((c) => !c.isRemote)) {
|
||||
const firstPush = !branch.upstream;
|
||||
const { refname, remote } = await branchController.pushBranch(
|
||||
const pushResult = await branchController.pushBranch(
|
||||
branch.id,
|
||||
branch.requiresForce
|
||||
branch.requiresForce,
|
||||
props.type === 'preview-series'
|
||||
);
|
||||
upstreamBranchName = getBranchNameFromRef(refname, remote);
|
||||
|
||||
if (pushResult) {
|
||||
upstreamBranchName = getBranchNameFromRef(pushResult.refname, pushResult.remote);
|
||||
}
|
||||
|
||||
if (firstPush) {
|
||||
// TODO: fix this hack for reactively available prService.
|
||||
@ -192,9 +208,9 @@
|
||||
baseBranchService.fetchFromRemotes();
|
||||
}
|
||||
|
||||
function handleCreatePR(close: () => void) {
|
||||
if (props.type !== 'preview') return;
|
||||
createPr({
|
||||
async function handleCreatePR(close: () => void) {
|
||||
if (props.type === 'display') return;
|
||||
await createPr({
|
||||
title: actualTitle,
|
||||
body: actualBody,
|
||||
draft: isDraft
|
||||
@ -212,7 +228,7 @@
|
||||
}
|
||||
|
||||
function toggleEdit() {
|
||||
if (props.type !== 'preview') return;
|
||||
if (props.type === 'display') return;
|
||||
isEditing = !isEditing;
|
||||
}
|
||||
|
||||
@ -228,7 +244,7 @@
|
||||
title: actualTitle,
|
||||
body: actualBody,
|
||||
directive: aiDescriptionDirective,
|
||||
commitMessages: branch.commits.map((c) => c.description),
|
||||
commitMessages: commits.map((c) => c.description),
|
||||
prBodyTemplate: pullRequestTemplateBody,
|
||||
userToken: $user.access_token,
|
||||
onToken: (t) => {
|
||||
@ -387,7 +403,7 @@
|
||||
</div>
|
||||
{/if}
|
||||
<div class="pr-modal__button-wrapper">
|
||||
{#if props.type === 'preview'}
|
||||
{#if props.type === 'preview' || props.type === 'preview-series'}
|
||||
<div class="pr-modal__checkbox-wrapper">
|
||||
<Checkbox name="is-draft" small checked={isDraft} onchange={handleCheckDraft} />
|
||||
<label class="text-13" for="is-draft">Draft</label>
|
||||
@ -402,7 +418,7 @@
|
||||
kind="solid"
|
||||
disabled={isEditing || isLoading || aiIsLoading}
|
||||
{isLoading}
|
||||
onclick={() => handleCreatePR(close)}
|
||||
onclick={async () => await handleCreatePR(close)}
|
||||
>{isDraft ? 'Create Draft PR' : 'Create PR'}</Button
|
||||
>
|
||||
{:else if props.type === 'display'}
|
||||
|
@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import MergeButton from './MergeButton.svelte';
|
||||
import PrDetailsModal from './PrDetailsModal.svelte';
|
||||
import ViewPrButton from './ViewPrButton.svelte';
|
||||
import InfoMessage from '../shared/InfoMessage.svelte';
|
||||
import { Project } from '$lib/backend/projects';
|
||||
@ -57,6 +58,7 @@
|
||||
// });
|
||||
|
||||
let isMerging = $state(false);
|
||||
let prDetailsModal = $state<ReturnType<typeof PrDetailsModal>>();
|
||||
|
||||
const lastFetch = $derived(prMonitor?.lastFetch);
|
||||
const timeAgo = $derived($lastFetch ? createTimeAgoStore($lastFetch) : undefined);
|
||||
@ -64,17 +66,9 @@
|
||||
const mrLoading = $derived(prMonitor?.loading);
|
||||
const checksLoading = $derived(checksMonitor?.loading);
|
||||
|
||||
$effect(() => {
|
||||
console.log($checksLoading);
|
||||
});
|
||||
|
||||
const checksError = $derived(checksMonitor?.error);
|
||||
const detailsError = $derived(prMonitor?.error);
|
||||
|
||||
$effect(() => {
|
||||
console.log($checksError);
|
||||
});
|
||||
|
||||
function getChecksCount(status: ChecksStatus): string {
|
||||
if (!status) return 'Running checks';
|
||||
|
||||
@ -198,7 +192,15 @@
|
||||
</div>
|
||||
<div class="text-13 text-semibold pr-header-title">
|
||||
<span style="color: var(--clr-scale-ntrl-50)">PR #{pr?.number}:</span>
|
||||
{pr.title}
|
||||
<Button
|
||||
style="ghost"
|
||||
outline
|
||||
onclick={() => {
|
||||
prDetailsModal?.show();
|
||||
}}
|
||||
>
|
||||
{pr.title}</Button
|
||||
>
|
||||
</div>
|
||||
<div class="pr-header-tags">
|
||||
<Button
|
||||
@ -275,6 +277,8 @@
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<PrDetailsModal bind:this={prDetailsModal} type="display" {pr} />
|
||||
{/if}
|
||||
|
||||
<style lang="postcss">
|
||||
|
@ -311,10 +311,10 @@ export class BranchController {
|
||||
branchId: string,
|
||||
withForce: boolean,
|
||||
stack: boolean = false
|
||||
): Promise<BranchPushResult> {
|
||||
): Promise<BranchPushResult | undefined> {
|
||||
try {
|
||||
const command = stack ? 'push_stack' : 'push_virtual_branch';
|
||||
const pushResult = await invoke<BranchPushResult>(command, {
|
||||
const pushResult = await invoke<BranchPushResult | undefined>(command, {
|
||||
projectId: this.projectId,
|
||||
branchId,
|
||||
withForce
|
||||
|
Loading…
Reference in New Issue
Block a user