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