Updated branch switch on the Settings page

- copy updated
- layout updated
- select component max-height fixed
This commit is contained in:
Pavel Laptev 2024-05-04 00:41:15 +02:00
parent fb6dd2429e
commit c0cc85688f
6 changed files with 134 additions and 148 deletions

View File

@ -75,6 +75,8 @@
e.stopPropagation();
}}
>
{isLaneCollapsed ? 'View branch' : `${$baseBranch.actualPushRemoteName()}/${$branch.upstreamName}`}
{isLaneCollapsed
? 'View branch'
: `${$baseBranch.actualPushRemoteName()}/${$branch.upstreamName}`}
</Tag>
{/if}

View File

@ -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}

View File

@ -26,7 +26,6 @@
let dragHandle: any;
let clone: any;
let isSwitching = false;
</script>
{#if $activeBranchesError}

View File

@ -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}>

View File

@ -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),

View File

@ -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 {