mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-12-18 06:22:28 +03:00
Updated branch switch on the Settings page
- copy updated - layout updated - select component max-height fixed
This commit is contained in:
parent
fb6dd2429e
commit
c0cc85688f
@ -75,6 +75,8 @@
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
{isLaneCollapsed ? 'View branch' : `${$baseBranch.actualPushRemoteName()}/${$branch.upstreamName}`}
|
||||
{isLaneCollapsed
|
||||
? 'View branch'
|
||||
: `${$baseBranch.actualPushRemoteName()}/${$branch.upstreamName}`}
|
||||
</Tag>
|
||||
{/if}
|
||||
|
@ -19,143 +19,123 @@
|
||||
|
||||
let project = getContext(Project);
|
||||
|
||||
let selectedBranch = {name: $baseBranch.branchName};
|
||||
let selectedRemote = {name: $baseBranch.actualPushRemoteName()};
|
||||
let selectedBranch = { name: $baseBranch.branchName };
|
||||
let selectedRemote = { name: $baseBranch.actualPushRemoteName() };
|
||||
let targetChangeDisabled = true;
|
||||
if ($activeBranches) {
|
||||
targetChangeDisabled = $activeBranches.length > 0;
|
||||
}
|
||||
let isSwitching = false;
|
||||
|
||||
// Fetch the remote branches reactively
|
||||
let remoteBranchesPromise: Promise<
|
||||
{
|
||||
name: string;
|
||||
}[]
|
||||
>;
|
||||
|
||||
$: {
|
||||
if (project) {
|
||||
remoteBranchesPromise = getRemoteBranches(project.id);
|
||||
}
|
||||
}
|
||||
|
||||
function uniqueRemotes(remoteBranches: { name: string }[]) {
|
||||
return Array.from(new Set(remoteBranches.map((b) => b.name.split('/')[0]))).map((r) => ({ name: r }));
|
||||
return Array.from(new Set(remoteBranches.map((b) => b.name.split('/')[0]))).map((r) => ({
|
||||
name: r
|
||||
}));
|
||||
}
|
||||
|
||||
async function onSetBaseBranchClick() {
|
||||
if (!selectedBranch) return;
|
||||
|
||||
// while target is setting, display loading
|
||||
isSwitching = true;
|
||||
isSwitching = true; // Indicate switching in progress
|
||||
|
||||
if(selectedRemote){
|
||||
await branchController
|
||||
.setTarget(selectedBranch.name, selectedRemote.name)
|
||||
.finally(() => {
|
||||
isSwitching = false;
|
||||
});
|
||||
if (selectedRemote) {
|
||||
await branchController.setTarget(selectedBranch.name, selectedRemote.name).finally(() => {
|
||||
isSwitching = false;
|
||||
});
|
||||
} else {
|
||||
await branchController
|
||||
.setTarget(selectedBranch.name)
|
||||
.finally(() => {
|
||||
isSwitching = false;
|
||||
});
|
||||
await branchController.setTarget(selectedBranch.name).finally(() => {
|
||||
isSwitching = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Section spacer>
|
||||
<SectionCard labelFor="targetBranch" orientation="column">
|
||||
<svelte:fragment slot="title">Current Target Branch</svelte:fragment>
|
||||
<svelte:fragment slot="caption">
|
||||
Your target branch is what you consider "production". This is where you
|
||||
want to integrate any branches that you create. Normally something like
|
||||
'origin/master' or 'upstream/main'.
|
||||
</svelte:fragment>
|
||||
{#await remoteBranchesPromise}
|
||||
<InfoMessage filled outlined={false} icon="info">
|
||||
<svelte:fragment slot="content">Loading remote branches...</svelte:fragment>
|
||||
</InfoMessage>
|
||||
{:then remoteBranches}
|
||||
{#if remoteBranches.length > 0}
|
||||
<Section spacer>
|
||||
<SectionCard>
|
||||
<svelte:fragment slot="title">Remote configuration</svelte:fragment>
|
||||
<svelte:fragment slot="caption">
|
||||
Lets you choose where to push code and set the target branch for contributions. The target
|
||||
branch is usually the "production" branch like 'origin/master' or 'upstream/main.' This
|
||||
section helps ensure your code goes to the correct remote and branch for integration.
|
||||
</svelte:fragment>
|
||||
|
||||
<div class="inputs-group">
|
||||
{#if isSwitching}
|
||||
<InfoMessage filled outlined={false} style="pop" icon="info">
|
||||
<svelte:fragment slot="title">
|
||||
Switching target branch...
|
||||
</svelte:fragment>
|
||||
</InfoMessage>
|
||||
{:else}
|
||||
{#await getRemoteBranches(project.id)}
|
||||
loading remote branches...
|
||||
{:then remoteBranches}
|
||||
{#if remoteBranches.length == 0}
|
||||
<InfoMessage filled outlined={false} style="error" icon="error">
|
||||
<svelte:fragment slot="title">
|
||||
You don't have any remote branches.
|
||||
</svelte:fragment>
|
||||
</InfoMessage>
|
||||
{:else}
|
||||
<div class="inputs-row">
|
||||
<Select
|
||||
items={remoteBranches}
|
||||
bind:value={selectedBranch}
|
||||
itemId="name"
|
||||
labelId="name"
|
||||
disabled={targetChangeDisabled}
|
||||
wide={true}
|
||||
>
|
||||
<SelectItem slot="template" let:item let:selected {selected}>
|
||||
{item.name}
|
||||
</SelectItem>
|
||||
</Select>
|
||||
<Button
|
||||
size="cta"
|
||||
style="ghost"
|
||||
kind="solid"
|
||||
on:click={onSetBaseBranchClick}
|
||||
id="set-base-branch"
|
||||
loading={isSwitching}
|
||||
disabled={(selectedBranch.name === $baseBranch.branchName) || targetChangeDisabled}
|
||||
>
|
||||
Change Target Branch
|
||||
</Button>
|
||||
</div>
|
||||
<Select
|
||||
items={remoteBranches}
|
||||
bind:value={selectedBranch}
|
||||
itemId="name"
|
||||
labelId="name"
|
||||
disabled={targetChangeDisabled}
|
||||
wide={true}
|
||||
label="Current target branch"
|
||||
>
|
||||
<SelectItem slot="template" let:item let:selected {selected}>
|
||||
{item.name}
|
||||
</SelectItem>
|
||||
</Select>
|
||||
|
||||
{#if uniqueRemotes(remoteBranches).length > 1}
|
||||
Create branches on remote:
|
||||
<Select
|
||||
items={uniqueRemotes(remoteBranches)}
|
||||
bind:value={selectedRemote}
|
||||
itemId="name"
|
||||
labelId="name"
|
||||
disabled={targetChangeDisabled}
|
||||
>
|
||||
<SelectItem slot="template" let:item let:selected {selected}>
|
||||
{item.name}
|
||||
</SelectItem>
|
||||
</Select>
|
||||
{/if}
|
||||
{#if uniqueRemotes(remoteBranches).length > 1}
|
||||
<Select
|
||||
items={uniqueRemotes(remoteBranches)}
|
||||
bind:value={selectedRemote}
|
||||
itemId="name"
|
||||
labelId="name"
|
||||
disabled={targetChangeDisabled}
|
||||
label="Create branches on remote"
|
||||
>
|
||||
<SelectItem slot="template" let:item let:selected {selected}>
|
||||
{item.name}
|
||||
</SelectItem>
|
||||
</Select>
|
||||
{/if}
|
||||
|
||||
{/if}
|
||||
{:catch}
|
||||
<InfoMessage filled outlined={true} style="error" icon="error">
|
||||
<svelte:fragment slot="title">
|
||||
We got an error trying to list your remote branches
|
||||
{#if $activeBranches && targetChangeDisabled}
|
||||
<InfoMessage filled outlined={false} icon="info">
|
||||
<svelte:fragment slot="content">
|
||||
You have {$activeBranches.length === 1
|
||||
? '1 active branch'
|
||||
: `${$activeBranches.length} active branches`} in your workspace. Please clear the workspace
|
||||
before switching the base branch.
|
||||
</svelte:fragment>
|
||||
</InfoMessage>
|
||||
{/await}
|
||||
{/if}
|
||||
|
||||
{#if $activeBranches && targetChangeDisabled}
|
||||
<InfoMessage filled outlined={false} icon="info">
|
||||
<svelte:fragment slot="content">
|
||||
You have {$activeBranches.length === 1
|
||||
? '1 active branch'
|
||||
: `${$activeBranches.length} active branches`} in your workspace. Please clear the workspace
|
||||
before switching the base branch.
|
||||
</svelte:fragment>
|
||||
</InfoMessage>
|
||||
{/if}
|
||||
</div>
|
||||
</SectionCard>
|
||||
</Section>
|
||||
|
||||
<style>
|
||||
.inputs-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--size-16);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.inputs-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: var(--size-16);
|
||||
}
|
||||
</style>
|
||||
{:else}
|
||||
<Button
|
||||
size="cta"
|
||||
style="ghost"
|
||||
kind="solid"
|
||||
on:click={onSetBaseBranchClick}
|
||||
id="set-base-branch"
|
||||
loading={isSwitching}
|
||||
disabled={selectedBranch.name === $baseBranch.branchName || targetChangeDisabled}
|
||||
>
|
||||
{isSwitching ? 'Switching branches...' : 'Update configuration'}
|
||||
</Button>
|
||||
{/if}
|
||||
</SectionCard>
|
||||
</Section>
|
||||
{/if}
|
||||
{:catch}
|
||||
<InfoMessage filled outlined={true} style="error" icon="error">
|
||||
<svelte:fragment slot="title"
|
||||
>We got an error trying to list your remote branches</svelte:fragment
|
||||
>
|
||||
</InfoMessage>
|
||||
{/await}
|
||||
|
@ -26,7 +26,6 @@
|
||||
|
||||
let dragHandle: any;
|
||||
let clone: any;
|
||||
let isSwitching = false;
|
||||
</script>
|
||||
|
||||
{#if $activeBranchesError}
|
||||
|
@ -36,35 +36,38 @@
|
||||
let loading = false;
|
||||
let selectedBranch = getBestBranch(remoteBranches);
|
||||
|
||||
function getBestBranch(branches: { name: string; }[]): { name: string } {
|
||||
// Function to calculate the rank of a branch
|
||||
// eslint-disable-next-line func-style
|
||||
const calculateRank = (branch: string): number => {
|
||||
if (branch === 'upstream/main' || branch === 'upstream/master') {
|
||||
return 100; // Highest preference
|
||||
}
|
||||
if (branch === 'origin/main' || branch === 'origin/master') {
|
||||
return 90;
|
||||
}
|
||||
if (branch.startsWith('origin')) {
|
||||
return 80;
|
||||
}
|
||||
if (branch.endsWith('master') || branch.endsWith('main')) {
|
||||
return 70;
|
||||
}
|
||||
return 10; // Least preference
|
||||
};
|
||||
function getBestBranch(branches: { name: string }[]): { name: string } {
|
||||
// Function to calculate the rank of a branch
|
||||
// eslint-disable-next-line func-style
|
||||
const calculateRank = (branch: string): number => {
|
||||
if (branch === 'upstream/main' || branch === 'upstream/master') {
|
||||
return 100; // Highest preference
|
||||
}
|
||||
if (branch === 'origin/main' || branch === 'origin/master') {
|
||||
return 90;
|
||||
}
|
||||
if (branch.startsWith('origin')) {
|
||||
return 80;
|
||||
}
|
||||
if (branch.endsWith('master') || branch.endsWith('main')) {
|
||||
return 70;
|
||||
}
|
||||
return 10; // Least preference
|
||||
};
|
||||
|
||||
// Sort the branches based on their rank
|
||||
branches.sort((a, b) => calculateRank(b.name) - calculateRank(a.name));
|
||||
// Sort the branches based on their rank
|
||||
branches.sort((a, b) => calculateRank(b.name) - calculateRank(a.name));
|
||||
|
||||
// Return the branch with the highest rank
|
||||
return branches[0];
|
||||
// Return the branch with the highest rank
|
||||
return branches[0];
|
||||
}
|
||||
|
||||
// split all the branches by the first '/' and gather the unique remote names
|
||||
// then turn remotes into an array of objects with a 'name' and 'value' key
|
||||
let remotes = Array.from(new Set(remoteBranches.map((b) => b.name.split('/')[0]))).map((r) => ({ name: r, value: r }));
|
||||
let remotes = Array.from(new Set(remoteBranches.map((b) => b.name.split('/')[0]))).map((r) => ({
|
||||
name: r,
|
||||
value: r
|
||||
}));
|
||||
let selectedRemote = remotes[0];
|
||||
|
||||
// if there's an 'origin', select it by default
|
||||
@ -95,8 +98,8 @@
|
||||
</Select>
|
||||
{#if remotes.length > 1}
|
||||
<p class="text-base-body-12">
|
||||
You have branches from multiple remotes. If you want to specify a push target for
|
||||
creating branches that is different from your production branch, change it here.
|
||||
You have branches from multiple remotes. If you want to specify a push target for creating
|
||||
branches that is different from your production branch, change it here.
|
||||
</p>
|
||||
<Select items={remotes} bind:value={selectedRemote} itemId="name" labelId="name">
|
||||
<SelectItem slot="template" let:item let:selected {selected}>
|
||||
|
@ -2,6 +2,7 @@
|
||||
import ScrollableContainer from './ScrollableContainer.svelte';
|
||||
import TextBox from './TextBox.svelte';
|
||||
import { clickOutside } from '$lib/clickOutside';
|
||||
import { pxToRem } from '$lib/utils/pxToRem';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
|
||||
export let id: undefined | string = undefined;
|
||||
@ -15,6 +16,7 @@
|
||||
export let value: any = undefined;
|
||||
export let selectedItemId: any = undefined;
|
||||
export let placeholder = '';
|
||||
export let maxHeight: number | undefined = 260;
|
||||
|
||||
$: if (selectedItemId) value = items.find((item) => item[itemId] == selectedItemId);
|
||||
|
||||
@ -25,7 +27,6 @@
|
||||
let listOpen = false;
|
||||
let element: HTMLElement;
|
||||
let options: HTMLDivElement;
|
||||
let maxHeight = 200;
|
||||
|
||||
function handleItemClick(item: any) {
|
||||
if (item?.selectable === false) return;
|
||||
@ -35,6 +36,7 @@
|
||||
listOpen = false;
|
||||
}
|
||||
function setMaxHeight() {
|
||||
if (maxHeight) return;
|
||||
maxHeight = window.innerHeight - element.getBoundingClientRect().bottom - maxPadding;
|
||||
}
|
||||
|
||||
@ -73,7 +75,7 @@
|
||||
class="options card"
|
||||
style:display={listOpen ? undefined : 'none'}
|
||||
bind:this={options}
|
||||
style:max-height={`${maxHeight}px`}
|
||||
style:max-height={maxHeight && pxToRem(maxHeight)}
|
||||
use:clickOutside={{
|
||||
trigger: element,
|
||||
handler: () => (listOpen = !listOpen),
|
||||
|
@ -1,11 +1,12 @@
|
||||
<script lang="ts">
|
||||
import Spacer from '../Spacer.svelte';
|
||||
export let spacer = false;
|
||||
export let gap = 'var(--size-20)';
|
||||
|
||||
const SLOTS = $$props.$$slots;
|
||||
</script>
|
||||
|
||||
<div class="settings-section">
|
||||
<div class="settings-section" style="gap: {gap}">
|
||||
{#if SLOTS.top}
|
||||
<slot name="top" />
|
||||
{/if}
|
||||
@ -36,7 +37,6 @@
|
||||
.settings-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--size-20);
|
||||
}
|
||||
|
||||
.description {
|
||||
|
Loading…
Reference in New Issue
Block a user