Put stashed branches in the sidenav again

This commit is contained in:
Mattias Granlund 2024-01-27 14:32:24 +01:00
parent a561a004f7
commit d9102716d8
8 changed files with 193 additions and 49 deletions

View File

@ -2,7 +2,7 @@ import type { PullRequest } from '$lib/github/types';
import type { Branch, RemoteBranch } from '$lib/vbranches/types';
import { CombinedBranch } from '$lib/branches/types';
import { Observable, combineLatest } from 'rxjs';
import { map, startWith, switchMap } from 'rxjs/operators';
import { startWith, switchMap } from 'rxjs/operators';
import type { RemoteBranchService } from '$lib/stores/remoteBranches';
import type { GitHubService } from '$lib/github/service';
import type { VirtualBranchService } from '$lib/vbranches/branchStoresCache';
@ -17,7 +17,7 @@ export class BranchService {
remoteBranchService: RemoteBranchService,
private githubService: GitHubService
) {
const vbranchesWithEmpty$ = vbranchService.activeBranches$.pipe(startWith([]));
const vbranchesWithEmpty$ = vbranchService.branches$.pipe(startWith([]));
const branchesWithEmpty$ = remoteBranchService.branches$.pipe(startWith([]));
const prWithEmpty$ = githubService.prs$.pipe(startWith([]));
@ -32,8 +32,7 @@ export class BranchService {
);
observer.next(contributions);
})
),
map((branches) => branches.filter((b) => !b.vbranch || b.vbranch.active))
)
);
}

View File

@ -18,7 +18,7 @@
$: branchService = data.branchService;
$: project$ = data.project$;
$: branches = vbranchService.branches$;
$: activeBranches$ = vbranchService.activeBranches$;
$: error$ = vbranchService.branchesError$;
$: githubEnabled$ = githubService.isEnabled$;
@ -63,7 +63,7 @@
project={$project$}
{cloud}
base={$base$}
branches={$branches}
branches={$activeBranches$}
projectPath={$project$?.path}
branchesError={$error$}
user={$user$}

View File

@ -120,7 +120,6 @@
{projectPath}
{user}
{githubService}
readonly={!branch.active}
></BranchLane>
</div>
{/each}

View File

@ -12,7 +12,7 @@
import Button from '$lib/components/Button.svelte';
import toast from 'svelte-french-toast';
import { tooltip } from '$lib/utils/tooltip';
import Toggle from '$lib/components/Toggle.svelte';
import { goto } from '$app/navigation';
export let readonly = false;
export let branch: Branch;
@ -27,6 +27,7 @@
let meatballButton: HTMLDivElement;
let visible = false;
let container: HTMLDivElement;
let isApplying = false;
function handleBranchNameChange() {
branchController.updateBranchName(branch.id, branch.name);
@ -160,44 +161,55 @@
Set as default
</Button>
{/if}
<Toggle
checked={!readonly}
help="Toggle to stash branch"
on:change={async () => {
try {
if (readonly) await branchController.applyBranch(branch.id);
else await branchController.unapplyBranch(branch.id);
} catch (e) {
const err = 'Failed to apply/unapply branch';
toast.error(err);
console.error(err, e);
}
}}
/>
</div>
<div class="relative" bind:this={meatballButton}>
<Button
icon="kebab"
kind="outlined"
color="neutral"
on:click={() => (visible = !visible)}
/>
<div
class="branch-popup-menu"
use:clickOutside={{
trigger: meatballButton,
handler: () => (visible = false)
}}
>
<BranchLanePopupMenu
{branchController}
{branch}
{projectId}
{readonly}
bind:visible
on:action
{#if readonly}
<Button
help="Restores these changes into your working directory"
icon="plus-small"
color="primary"
kind="outlined"
loading={isApplying}
on:click={async () => {
isApplying = true;
try {
await branchController.applyBranch(branch.id);
goto(`/${projectId}/board`);
} catch (e) {
const err = 'Failed to apply branch';
toast.error(err);
console.error(err, e);
} finally {
isApplying = false;
}
}}
>
Apply
</Button>
{:else}
<Button
icon="kebab"
kind="outlined"
color="neutral"
on:click={() => (visible = !visible)}
/>
</div>
<div
class="branch-popup-menu"
use:clickOutside={{
trigger: meatballButton,
handler: () => (visible = false)
}}
>
<BranchLanePopupMenu
{branchController}
{branch}
{projectId}
{readonly}
bind:visible
on:action
/>
</div>
{/if}
</div>
</div>
</div>
@ -257,8 +269,7 @@
.header__buttons {
display: flex;
position: relative;
align-items: center;
gap: var(--space-10);
gap: var(--space-4);
}
.header__label {
display: flex;

View File

@ -11,6 +11,7 @@
export let includePrs: Writable<boolean | undefined>;
export let includeRemote: Writable<boolean | undefined>;
export let includeStashed: Writable<boolean | undefined>;
export let hideBots: Writable<boolean | undefined>;
export let hideInactive: Writable<boolean | undefined>;
</script>
@ -26,6 +27,10 @@
<ContextMenuItem label="Remote" on:click={() => ($includeRemote = !$includeRemote)}>
<Checkbox small bind:checked={$includeRemote} slot="control" />
</ContextMenuItem>
<ContextMenuItem label="Stashed" on:click={() => ($includeStashed = !$includeStashed)}>
<Checkbox small bind:checked={$includeStashed} slot="control" />
</ContextMenuItem>
</ContextMenuSection>
<ContextMenuSection>

View File

@ -31,13 +31,14 @@
let includePrs = persisted(true, 'includePrs_' + projectId);
let includeRemote = persisted(true, 'includeRemote_' + projectId);
let includeStashed = persisted(true, 'includeStashed_' + projectId);
let hideBots = persisted(false, 'hideBots_' + projectId);
let hideInactive = persisted(false, 'hideInactive_' + projectId);
let filtersActive = derived(
[includePrs, includeRemote, hideBots, hideInactive],
([prs, remote, bots, inactive]) => {
return !prs || !remote || bots || inactive;
[includePrs, includeRemote, includeStashed, hideBots, hideInactive],
([prs, remote, stashed, bots, inactive]) => {
return !prs || !remote || !stashed || bots || inactive;
}
);
@ -48,16 +49,19 @@
textFilter$,
storeToObservable(includePrs),
storeToObservable(includeRemote),
storeToObservable(includeStashed),
storeToObservable(hideBots),
storeToObservable(hideInactive)
],
(branches, search, includePrs, includeRemote, hideBots, hideInactive) => {
(branches, search, includePrs, includeRemote, includeStashed, hideBots, hideInactive) => {
const filteredByType = filterByType(branches, {
includePrs,
includeRemote,
includeStashed,
hideBots
});
const filteredBySearch = filterByText(filteredByType, search);
console.log(branches);
return hideInactive ? filterInactive(filteredBySearch) : filteredBySearch;
}
);
@ -75,6 +79,7 @@
params: {
includePrs: boolean;
includeRemote: boolean;
includeStashed: boolean;
hideBots: boolean;
}
): CombinedBranch[] {
@ -83,6 +88,7 @@
return !params.hideBots || !b.pr.author?.isBot;
}
if (params.includeRemote && b.remoteBranch) return true;
if (params.includeStashed && b.vbranch) return true;
return false;
});
}
@ -149,6 +155,7 @@
{visible}
{includePrs}
{includeRemote}
{includeStashed}
{hideBots}
{hideInactive}
showPrCheckbox={$githubEnabled$}

View File

@ -0,0 +1,37 @@
<script lang="ts">
import { page } from '$app/stores';
import Icon from '$lib/icons/Icon.svelte';
import type { Branch } from '$lib/vbranches/types';
import { slide } from 'svelte/transition';
export let branch: Branch;
export let projectId: string;
$: href = `/${projectId}/stashed/${branch.id}`;
$: selected = $page.url.href.includes(href);
</script>
<a class="item" {href} class:selected transition:slide={{ duration: 250 }}>
<Icon name="branch" />
<div class="text-color-2 flex-grow truncate">
{branch.name}
{branch.files[0]?.modifiedAt}
</div>
</a>
<style lang="postcss">
.item {
display: flex;
gap: var(--space-10);
padding-top: var(--space-10);
padding-bottom: var(--space-10);
padding-left: var(--space-8);
padding-right: var(--space-8);
border-radius: var(--radius-m);
}
.item:hover,
.item:focus,
.selected {
background-color: var(--clr-theme-container-pale);
}
</style>

View File

@ -0,0 +1,86 @@
<script lang="ts">
import type { PageData } from './$types';
import { page } from '$app/stores';
import BranchLane from '../../components/BranchLane.svelte';
import Button from '$lib/components/Button.svelte';
import Modal from '$lib/components/Modal.svelte';
import { goto } from '$app/navigation';
export let data: PageData;
let applyConflictedModal: Modal;
let deleteBranchModal: Modal;
$: projectId = data.projectId;
$: user$ = data.user$;
$: cloud = data.cloud;
$: project$ = data.project$;
$: branchController = data.branchController;
$: vbranchService = data.vbranchService;
$: baseBranchService = data.baseBranchService;
$: baseBranch$ = baseBranchService.base$;
$: branchService = data.branchService;
$: branches$ = vbranchService.branches$;
$: error$ = vbranchService.branchesError$;
$: branch = $branches$?.find((b) => b.id == $page.params.branchId);
$: githubService = data.githubService;
</script>
{#if $error$}
<p>Error...</p>
{:else if !$branches$}
<p>Loading...</p>
{:else if branch}
<BranchLane
{branch}
{branchController}
{branchService}
base={$baseBranch$}
{cloud}
project={$project$}
readonly={!branch.active}
user={$user$}
projectPath={$project$.path}
{githubService}
/>
{:else}
<p>Branch no longer exists</p>
{/if}
<Modal width="small" title="Merge conflicts" bind:this={applyConflictedModal}>
<p>Applying this branch will introduce merge conflicts.</p>
<svelte:fragment slot="controls" let:item let:close>
<Button kind="outlined" color="neutral" on:click={close}>Cancel</Button>
<Button
color="primary"
on:click={() => {
branchController.applyBranch(item.id);
close();
goto(`/${projectId}/board`);
}}
>
Update
</Button>
</svelte:fragment>
</Modal>
<Modal width="small" title="Delete branch" bind:this={deleteBranchModal} let:item>
<div>
Deleting <code>{item.name}</code> cannot be undone.
</div>
<svelte:fragment slot="controls" let:close let:item>
<Button kind="outlined" color="neutral" on:click={close}>Cancel</Button>
<Button
color="error"
on:click={() => {
branchController.deleteBranch(item.id);
close();
goto(`/${projectId}/board`);
}}
>
Delete
</Button>
</svelte:fragment>
</Modal>