mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2025-01-04 15:53:30 +03:00
🔨 chore: update PrService to support injecting and retrieving additional Pull Requests
This commit is contained in:
parent
2c08ae499a
commit
cc9a6a5b70
@ -1,6 +1,14 @@
|
|||||||
import lscache from 'lscache';
|
import lscache from 'lscache';
|
||||||
import { Observable, EMPTY, BehaviorSubject, of } from 'rxjs';
|
import { Observable, EMPTY, BehaviorSubject, of } from 'rxjs';
|
||||||
import { catchError, combineLatestWith, shareReplay, switchMap, tap } from 'rxjs/operators';
|
import {
|
||||||
|
catchError,
|
||||||
|
combineLatestWith,
|
||||||
|
find,
|
||||||
|
map,
|
||||||
|
shareReplay,
|
||||||
|
switchMap,
|
||||||
|
tap
|
||||||
|
} from 'rxjs/operators';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
type PullRequest,
|
type PullRequest,
|
||||||
@ -13,6 +21,7 @@ export class PrService {
|
|||||||
prs$: Observable<PullRequest[]>;
|
prs$: Observable<PullRequest[]>;
|
||||||
error$ = new BehaviorSubject<string | undefined>(undefined);
|
error$ = new BehaviorSubject<string | undefined>(undefined);
|
||||||
private reload$ = new BehaviorSubject<void>(undefined);
|
private reload$ = new BehaviorSubject<void>(undefined);
|
||||||
|
private inject$ = new BehaviorSubject<PullRequest | undefined>(undefined);
|
||||||
|
|
||||||
constructor(ghContext$: Observable<GitHubIntegrationContext | undefined>) {
|
constructor(ghContext$: Observable<GitHubIntegrationContext | undefined>) {
|
||||||
this.prs$ = ghContext$.pipe(
|
this.prs$ = ghContext$.pipe(
|
||||||
@ -23,6 +32,11 @@ export class PrService {
|
|||||||
return loadPrs(ctx);
|
return loadPrs(ctx);
|
||||||
}),
|
}),
|
||||||
shareReplay(1),
|
shareReplay(1),
|
||||||
|
combineLatestWith(this.inject$),
|
||||||
|
map(([prs, inject]) => {
|
||||||
|
if (inject) return prs.concat(inject);
|
||||||
|
return prs;
|
||||||
|
}),
|
||||||
catchError((err) => {
|
catchError((err) => {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
this.error$.next(err);
|
this.error$.next(err);
|
||||||
@ -34,6 +48,13 @@ export class PrService {
|
|||||||
reload(): void {
|
reload(): void {
|
||||||
this.reload$.next();
|
this.reload$.next();
|
||||||
}
|
}
|
||||||
|
add(pr: PullRequest) {
|
||||||
|
this.inject$.next(pr);
|
||||||
|
}
|
||||||
|
get(branch: string | undefined): Observable<PullRequest | undefined> | undefined {
|
||||||
|
if (!branch) return;
|
||||||
|
return this.prs$.pipe(map((prs) => prs.find((pr) => pr.sourceBranch == branch)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadPrs(ctx: GitHubIntegrationContext): Observable<PullRequest[]> {
|
function loadPrs(ctx: GitHubIntegrationContext): Observable<PullRequest[]> {
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
$: projectId = data.projectId;
|
$: projectId = data.projectId;
|
||||||
$: base$ = baseBranchService.base$;
|
$: base$ = baseBranchService.base$;
|
||||||
$: user$ = data.user$;
|
$: user$ = data.user$;
|
||||||
|
$: prService = data.prService;
|
||||||
|
|
||||||
$: project$ = data.project$;
|
$: project$ = data.project$;
|
||||||
$: branches$ = vbranchService.branches$;
|
$: branches$ = vbranchService.branches$;
|
||||||
@ -59,6 +60,7 @@
|
|||||||
githubContext={$githubContext$}
|
githubContext={$githubContext$}
|
||||||
branchesError={$error$}
|
branchesError={$error$}
|
||||||
user={$user$}
|
user={$user$}
|
||||||
|
{prService}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
import { open } from '@tauri-apps/api/shell';
|
import { open } from '@tauri-apps/api/shell';
|
||||||
import { IconFile, IconTerminal, IconExternalLink } from '$lib/icons';
|
import { IconFile, IconTerminal, IconExternalLink } from '$lib/icons';
|
||||||
import type { GitHubIntegrationContext } from '$lib/github/types';
|
import type { GitHubIntegrationContext } from '$lib/github/types';
|
||||||
|
import type { PrService } from '$lib/github/pullrequest';
|
||||||
|
|
||||||
export let projectId: string;
|
export let projectId: string;
|
||||||
export let projectPath: string;
|
export let projectPath: string;
|
||||||
@ -19,6 +20,7 @@
|
|||||||
export let cloudEnabled: boolean;
|
export let cloudEnabled: boolean;
|
||||||
export let cloud: ReturnType<typeof getCloudApiClient>;
|
export let cloud: ReturnType<typeof getCloudApiClient>;
|
||||||
export let branchController: BranchController;
|
export let branchController: BranchController;
|
||||||
|
export let prService: PrService;
|
||||||
|
|
||||||
export let githubContext: GitHubIntegrationContext | undefined;
|
export let githubContext: GitHubIntegrationContext | undefined;
|
||||||
export let user: User | undefined;
|
export let user: User | undefined;
|
||||||
@ -101,6 +103,7 @@
|
|||||||
{githubContext}
|
{githubContext}
|
||||||
{projectPath}
|
{projectPath}
|
||||||
{user}
|
{user}
|
||||||
|
{prService}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
|
@ -22,15 +22,13 @@
|
|||||||
import CommitDialog from './CommitDialog.svelte';
|
import CommitDialog from './CommitDialog.svelte';
|
||||||
import { writable, type Writable } from 'svelte/store';
|
import { writable, type Writable } from 'svelte/store';
|
||||||
import { computedAddedRemoved } from '$lib/vbranches/fileStatus';
|
import { computedAddedRemoved } from '$lib/vbranches/fileStatus';
|
||||||
import { getPullRequestByBranch, createPullRequest } from '$lib/github/pullrequest';
|
import type { PrService } from '$lib/github/pullrequest';
|
||||||
import type { GitHubIntegrationContext } from '$lib/github/types';
|
import type { GitHubIntegrationContext } from '$lib/github/types';
|
||||||
import { isDraggableRemoteCommit, type DraggableRemoteCommit } from '$lib/draggables';
|
import { isDraggableRemoteCommit, type DraggableRemoteCommit } from '$lib/draggables';
|
||||||
import BranchHeader from './BranchHeader.svelte';
|
import BranchHeader from './BranchHeader.svelte';
|
||||||
import UpstreamCommits from './UpstreamCommits.svelte';
|
import UpstreamCommits from './UpstreamCommits.svelte';
|
||||||
import BranchFiles from './BranchFiles.svelte';
|
import BranchFiles from './BranchFiles.svelte';
|
||||||
import LocalCommits from './LocalCommits.svelte';
|
import LocalCommits from './LocalCommits.svelte';
|
||||||
import RemoteCommits from './RemoteCommits.svelte';
|
|
||||||
import IntegratedCommits from './IntegratedCommits.svelte';
|
|
||||||
|
|
||||||
const [send, receive] = crossfade({
|
const [send, receive] = crossfade({
|
||||||
duration: (d) => Math.sqrt(d * 200),
|
duration: (d) => Math.sqrt(d * 200),
|
||||||
@ -62,6 +60,7 @@
|
|||||||
export let githubContext: GitHubIntegrationContext | undefined;
|
export let githubContext: GitHubIntegrationContext | undefined;
|
||||||
export let user: User | undefined;
|
export let user: User | undefined;
|
||||||
export let selectedFileId: Writable<string | undefined>;
|
export let selectedFileId: Writable<string | undefined>;
|
||||||
|
export let prService: PrService;
|
||||||
|
|
||||||
const userSettings = getContext<SettingsStore>(SETTINGS_CONTEXT);
|
const userSettings = getContext<SettingsStore>(SETTINGS_CONTEXT);
|
||||||
|
|
||||||
@ -74,29 +73,6 @@
|
|||||||
const laneWidthKey = 'laneWidth:';
|
const laneWidthKey = 'laneWidth:';
|
||||||
let laneWidth: number;
|
let laneWidth: number;
|
||||||
|
|
||||||
$: prPromise =
|
|
||||||
githubContext && branch.upstream
|
|
||||||
? getPullRequestByBranch(githubContext, branch.upstream?.name.split('/').slice(-1)[0])
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
$: branchName = branch.upstream?.name.split('/').slice(-1)[0];
|
|
||||||
|
|
||||||
async function createPr() {
|
|
||||||
if (githubContext && base?.branchName && branchName) {
|
|
||||||
console.log('creating pr');
|
|
||||||
const pr = await createPullRequest(
|
|
||||||
githubContext,
|
|
||||||
branchName,
|
|
||||||
base.branchName.split('/').slice(-1)[0],
|
|
||||||
branch.name,
|
|
||||||
branch.notes
|
|
||||||
);
|
|
||||||
console.log(pr);
|
|
||||||
prPromise = Promise.resolve(pr);
|
|
||||||
return pr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$: {
|
$: {
|
||||||
// On refresh we need to check expansion status from localStorage
|
// On refresh we need to check expansion status from localStorage
|
||||||
branch.files && expandFromCache();
|
branch.files && expandFromCache();
|
||||||
@ -355,7 +331,6 @@
|
|||||||
{base}
|
{base}
|
||||||
{send}
|
{send}
|
||||||
{receive}
|
{receive}
|
||||||
{prPromise}
|
|
||||||
{githubContext}
|
{githubContext}
|
||||||
{projectId}
|
{projectId}
|
||||||
{branchController}
|
{branchController}
|
||||||
@ -364,7 +339,7 @@
|
|||||||
{onAmend}
|
{onAmend}
|
||||||
{onSquash}
|
{onSquash}
|
||||||
{resetHeadCommit}
|
{resetHeadCommit}
|
||||||
{createPr}
|
{prService}
|
||||||
type="local"
|
type="local"
|
||||||
/>
|
/>
|
||||||
<LocalCommits
|
<LocalCommits
|
||||||
@ -372,7 +347,6 @@
|
|||||||
{base}
|
{base}
|
||||||
{send}
|
{send}
|
||||||
{receive}
|
{receive}
|
||||||
{prPromise}
|
|
||||||
{githubContext}
|
{githubContext}
|
||||||
{projectId}
|
{projectId}
|
||||||
{branchController}
|
{branchController}
|
||||||
@ -381,7 +355,7 @@
|
|||||||
{onAmend}
|
{onAmend}
|
||||||
{onSquash}
|
{onSquash}
|
||||||
{resetHeadCommit}
|
{resetHeadCommit}
|
||||||
{createPr}
|
{prService}
|
||||||
type="remote"
|
type="remote"
|
||||||
/>
|
/>
|
||||||
<LocalCommits
|
<LocalCommits
|
||||||
@ -389,7 +363,6 @@
|
|||||||
{base}
|
{base}
|
||||||
{send}
|
{send}
|
||||||
{receive}
|
{receive}
|
||||||
{prPromise}
|
|
||||||
{githubContext}
|
{githubContext}
|
||||||
{projectId}
|
{projectId}
|
||||||
{branchController}
|
{branchController}
|
||||||
@ -398,7 +371,7 @@
|
|||||||
{onAmend}
|
{onAmend}
|
||||||
{onSquash}
|
{onSquash}
|
||||||
{resetHeadCommit}
|
{resetHeadCommit}
|
||||||
{createPr}
|
{prService}
|
||||||
type="integrated"
|
type="integrated"
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
import FileCard from './FileCard.svelte';
|
import FileCard from './FileCard.svelte';
|
||||||
import { writable } from 'svelte/store';
|
import { writable } from 'svelte/store';
|
||||||
import { Ownership } from '$lib/vbranches/ownership';
|
import { Ownership } from '$lib/vbranches/ownership';
|
||||||
|
import type { PrService } from '$lib/github/pullrequest';
|
||||||
|
|
||||||
export let branch: Branch;
|
export let branch: Branch;
|
||||||
export let readonly = false;
|
export let readonly = false;
|
||||||
@ -20,6 +21,7 @@
|
|||||||
export let githubContext: GitHubIntegrationContext | undefined;
|
export let githubContext: GitHubIntegrationContext | undefined;
|
||||||
export let user: User | undefined;
|
export let user: User | undefined;
|
||||||
export let projectPath: string;
|
export let projectPath: string;
|
||||||
|
export let prService: PrService;
|
||||||
|
|
||||||
const selectedOwnership = writable(Ownership.fromBranch(branch));
|
const selectedOwnership = writable(Ownership.fromBranch(branch));
|
||||||
const selectedFileId = writable<string | undefined>(undefined);
|
const selectedFileId = writable<string | undefined>(undefined);
|
||||||
@ -48,6 +50,7 @@
|
|||||||
{githubContext}
|
{githubContext}
|
||||||
{user}
|
{user}
|
||||||
{selectedFileId}
|
{selectedFileId}
|
||||||
|
{prService}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{#if selected}
|
{#if selected}
|
||||||
|
@ -18,21 +18,21 @@
|
|||||||
import { open } from '@tauri-apps/api/shell';
|
import { open } from '@tauri-apps/api/shell';
|
||||||
import toast from 'svelte-french-toast';
|
import toast from 'svelte-french-toast';
|
||||||
import { sleep } from '$lib/utils/sleep';
|
import { sleep } from '$lib/utils/sleep';
|
||||||
|
import { createPullRequest, type PrService } from '$lib/github/pullrequest';
|
||||||
|
|
||||||
export let branch: Branch;
|
export let branch: Branch;
|
||||||
export let githubContext: GitHubIntegrationContext | undefined;
|
export let githubContext: GitHubIntegrationContext | undefined;
|
||||||
export let base: BaseBranch | undefined | null;
|
export let base: BaseBranch | undefined | null;
|
||||||
export let prPromise: Promise<PullRequest | undefined> | undefined;
|
|
||||||
export let projectId: string;
|
export let projectId: string;
|
||||||
export let branchController: BranchController;
|
export let branchController: BranchController;
|
||||||
export let type: CommitType;
|
export let type: CommitType;
|
||||||
|
export let prService: PrService;
|
||||||
|
|
||||||
export let acceptAmend: (commit: Commit) => (data: any) => boolean;
|
export let acceptAmend: (commit: Commit) => (data: any) => boolean;
|
||||||
export let acceptSquash: (commit: Commit) => (data: any) => boolean;
|
export let acceptSquash: (commit: Commit) => (data: any) => boolean;
|
||||||
export let onAmend: (data: DraggableFile | DraggableHunk) => void;
|
export let onAmend: (data: DraggableFile | DraggableHunk) => void;
|
||||||
export let onSquash: (commit: Commit) => (data: DraggableCommit) => void;
|
export let onSquash: (commit: Commit) => (data: DraggableCommit) => void;
|
||||||
export let resetHeadCommit: () => void;
|
export let resetHeadCommit: () => void;
|
||||||
export let createPr: () => Promise<PullRequest | undefined>;
|
|
||||||
|
|
||||||
export let receive: (
|
export let receive: (
|
||||||
node: any,
|
node: any,
|
||||||
@ -50,6 +50,7 @@
|
|||||||
|
|
||||||
let isPushing: boolean;
|
let isPushing: boolean;
|
||||||
|
|
||||||
|
$: branchName = branch.upstream?.name.split('/').slice(-1)[0];
|
||||||
$: headCommit = branch.commits[0];
|
$: headCommit = branch.commits[0];
|
||||||
$: commits = branch.commits.filter((c) => {
|
$: commits = branch.commits.filter((c) => {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
@ -61,6 +62,7 @@
|
|||||||
return c.isIntegrated;
|
return c.isIntegrated;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
$: pr$ = prService.get(branchName);
|
||||||
|
|
||||||
async function push(opts?: { createPr: boolean }) {
|
async function push(opts?: { createPr: boolean }) {
|
||||||
isPushing = true;
|
isPushing = true;
|
||||||
@ -80,6 +82,23 @@
|
|||||||
return `${target.repoBaseUrl}/compare/${baseBranchName}...${branchName}`;
|
return `${target.repoBaseUrl}/compare/${baseBranchName}...${branchName}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function createPr() {
|
||||||
|
if (githubContext && base?.branchName && branchName) {
|
||||||
|
const pr = await createPullRequest(
|
||||||
|
githubContext,
|
||||||
|
branchName,
|
||||||
|
base.branchName.split('/').slice(-1)[0],
|
||||||
|
branch.name,
|
||||||
|
branch.notes
|
||||||
|
);
|
||||||
|
if (pr) {
|
||||||
|
prService.add(pr);
|
||||||
|
prService.reload();
|
||||||
|
}
|
||||||
|
return pr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let expanded = true;
|
let expanded = true;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -99,22 +118,21 @@
|
|||||||
>
|
>
|
||||||
{branch.upstream.name.split('refs/remotes/')[1]}
|
{branch.upstream.name.split('refs/remotes/')[1]}
|
||||||
</Link>
|
</Link>
|
||||||
{#await prPromise then pr}
|
{#if $pr$?.htmlUrl}
|
||||||
{#if pr?.htmlUrl}
|
<Tag
|
||||||
<Tag
|
icon="pr-small"
|
||||||
icon="pr-small"
|
color="neutral-light"
|
||||||
color="neutral-light"
|
clickable
|
||||||
clickable
|
on:click={(e) => {
|
||||||
on:click={(e) => {
|
const url = $pr$?.htmlUrl;
|
||||||
open(pr?.htmlUrl);
|
if (url) open(url);
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
PR
|
PR
|
||||||
</Tag>
|
</Tag>
|
||||||
{/if}
|
{/if}
|
||||||
{/await}
|
|
||||||
{/if}
|
{/if}
|
||||||
{:else if type == 'integrated'}
|
{:else if type == 'integrated'}
|
||||||
Integrated
|
Integrated
|
||||||
@ -138,73 +156,71 @@
|
|||||||
{commit}
|
{commit}
|
||||||
{base}
|
{base}
|
||||||
{projectId}
|
{projectId}
|
||||||
isChained={idx != commits.length - 1}
|
|
||||||
isHeadCommit={commit.id === headCommit?.id}
|
|
||||||
{acceptAmend}
|
{acceptAmend}
|
||||||
{acceptSquash}
|
{acceptSquash}
|
||||||
{onAmend}
|
{onAmend}
|
||||||
{onSquash}
|
{onSquash}
|
||||||
{resetHeadCommit}
|
{resetHeadCommit}
|
||||||
|
isChained={idx != commits.length - 1}
|
||||||
|
isHeadCommit={commit.id === headCommit?.id}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{#if type != 'integrated'}
|
{#if type != 'integrated'}
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
{#await prPromise then pr}
|
{#if githubContext && !$pr$ && type == 'local'}
|
||||||
{#if githubContext && !pr && type == 'local'}
|
<PushButton
|
||||||
<PushButton
|
wide
|
||||||
wide
|
isLoading={isPushing}
|
||||||
isLoading={isPushing}
|
{projectId}
|
||||||
{projectId}
|
{githubContext}
|
||||||
{githubContext}
|
on:trigger={async (e) => {
|
||||||
on:trigger={async (e) => {
|
try {
|
||||||
try {
|
await push({ createPr: e.detail.with_pr });
|
||||||
await push({ createPr: e.detail.with_pr });
|
} catch {
|
||||||
} catch {
|
toast.error('Failed to create pull qequest');
|
||||||
toast.error('Failed to create pull qequest');
|
}
|
||||||
}
|
}}
|
||||||
}}
|
/>
|
||||||
/>
|
{:else if githubContext && !$pr$ && type == 'remote'}
|
||||||
{:else if githubContext && !pr && type == 'remote'}
|
<Button
|
||||||
<Button
|
wide
|
||||||
wide
|
kind="outlined"
|
||||||
kind="outlined"
|
color="primary"
|
||||||
color="primary"
|
id="push-commits"
|
||||||
id="push-commits"
|
loading={isPushing}
|
||||||
loading={isPushing}
|
on:click={async () => {
|
||||||
on:click={async () => {
|
try {
|
||||||
try {
|
await push({ createPr: true });
|
||||||
await push({ createPr: true });
|
} catch (e) {
|
||||||
} catch (e) {
|
toast.error('Failed to create pull qequest');
|
||||||
toast.error('Failed to create pull qequest');
|
}
|
||||||
}
|
}}
|
||||||
}}
|
>
|
||||||
>
|
Create Pull Request
|
||||||
Create Pull Request
|
</Button>
|
||||||
</Button>
|
{:else if type == 'local'}
|
||||||
{:else if type == 'local'}
|
<Button
|
||||||
<Button
|
kind="outlined"
|
||||||
kind="outlined"
|
color="primary"
|
||||||
color="primary"
|
id="push-commits"
|
||||||
id="push-commits"
|
loading={isPushing}
|
||||||
loading={isPushing}
|
on:click={async () => {
|
||||||
on:click={async () => {
|
try {
|
||||||
try {
|
await push();
|
||||||
await push();
|
} catch {
|
||||||
} catch {
|
toast.error('Failed to push');
|
||||||
toast.error('Failed to push');
|
}
|
||||||
}
|
}}
|
||||||
}}
|
>
|
||||||
>
|
{#if branch.requiresForce}
|
||||||
{#if branch.requiresForce}
|
Force Push
|
||||||
Force Push
|
{:else}
|
||||||
{:else}
|
Push
|
||||||
Push
|
{/if}
|
||||||
{/if}
|
</Button>
|
||||||
</Button>
|
{/if}
|
||||||
{/if}
|
|
||||||
{/await}
|
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
$: branches$ = vbranchService.branches$;
|
$: branches$ = vbranchService.branches$;
|
||||||
$: error$ = vbranchService.branchesError$;
|
$: error$ = vbranchService.branchesError$;
|
||||||
$: branch = $branches$?.find((b) => b.id == $page.params.branchId);
|
$: branch = $branches$?.find((b) => b.id == $page.params.branchId);
|
||||||
|
$: prService = data.prService;
|
||||||
|
|
||||||
function applyBranch(branch: Branch) {
|
function applyBranch(branch: Branch) {
|
||||||
if (!branch.isMergeable) {
|
if (!branch.isMergeable) {
|
||||||
@ -80,6 +81,7 @@
|
|||||||
githubContext={$githubContext$}
|
githubContext={$githubContext$}
|
||||||
user={$user$}
|
user={$user$}
|
||||||
projectPath={$project$.path}
|
projectPath={$project$.path}
|
||||||
|
{prService}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
|
Loading…
Reference in New Issue
Block a user