mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-12-19 15:41:31 +03:00
Use SectionCard instead of ClickableCard
- commit grew a bit in scope - lots of refactoring to settings pages - clickable card dropped
This commit is contained in:
parent
0311052f24
commit
645e076a78
12
gitbutler-ui/src/lib/backend/auth.ts
Normal file
12
gitbutler-ui/src/lib/backend/auth.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { invoke } from './ipc';
|
||||
|
||||
export type GitCredentialCheck = {
|
||||
error?: string;
|
||||
ok: boolean;
|
||||
};
|
||||
|
||||
export class AuthService {
|
||||
async getPublicKey() {
|
||||
return await invoke<string>('get_public_key');
|
||||
}
|
||||
}
|
@ -16,13 +16,12 @@ import { get } from 'svelte/store';
|
||||
import type { Project as CloudProject } from '$lib/backend/cloud';
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
export type Key =
|
||||
| 'default'
|
||||
| 'generated'
|
||||
| 'gitCredentialsHelper'
|
||||
| {
|
||||
local: { private_key_path: string; passphrase?: string };
|
||||
};
|
||||
export type KeyType = 'default' | 'generated' | 'gitCredentialsHelper' | 'local';
|
||||
export type LocalKey = {
|
||||
local: { private_key_path: string; passphrase?: string };
|
||||
};
|
||||
|
||||
export type Key = Exclude<KeyType, 'local'> | LocalKey;
|
||||
|
||||
export type Project = {
|
||||
id: string;
|
||||
|
@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import ClickableCard from './ClickableCard.svelte';
|
||||
import InfoMessage from './InfoMessage.svelte';
|
||||
import Link from './Link.svelte';
|
||||
import SectionCard from './SectionCard.svelte';
|
||||
import Toggle from './Toggle.svelte';
|
||||
import { appErrorReportingEnabled, appMetricsEnabled } from '$lib/config/appSettings';
|
||||
|
||||
@ -39,7 +39,7 @@
|
||||
</div>
|
||||
|
||||
<div class="analytics-settings__actions">
|
||||
<ClickableCard on:click={toggleErrorReporting}>
|
||||
<SectionCard on:click={toggleErrorReporting} orientation="row">
|
||||
<svelte:fragment slot="title">Error reporting</svelte:fragment>
|
||||
<svelte:fragment slot="body">
|
||||
Toggle reporting of application crashes and errors.
|
||||
@ -47,15 +47,15 @@
|
||||
<svelte:fragment slot="actions">
|
||||
<Toggle checked={$errorReportingEnabled} on:change={toggleErrorReporting} />
|
||||
</svelte:fragment>
|
||||
</ClickableCard>
|
||||
</SectionCard>
|
||||
|
||||
<ClickableCard on:click={toggleMetrics}>
|
||||
<SectionCard on:click={toggleMetrics} orientation="row">
|
||||
<svelte:fragment slot="title">Usage metrics</svelte:fragment>
|
||||
<svelte:fragment slot="body">Toggle sharing of usage statistics.</svelte:fragment>
|
||||
<svelte:fragment slot="actions">
|
||||
<Toggle checked={$metricsEnabled} on:change={toggleMetrics} />
|
||||
</svelte:fragment>
|
||||
</ClickableCard>
|
||||
</SectionCard>
|
||||
|
||||
{#if updatedTelemetrySettings}
|
||||
<InfoMessage>Changes will take effect on the next application start.</InfoMessage>
|
||||
|
@ -1,119 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
|
||||
export let padding: string = 'var(--space-16)';
|
||||
export let disabled = false;
|
||||
export let checked = false;
|
||||
export let hasTopRadius = true;
|
||||
export let hasBottomRadius = true;
|
||||
export let hasBottomLine = true;
|
||||
|
||||
const SLOTS = $$props.$$slots;
|
||||
|
||||
const dispatchClick = createEventDispatcher<{
|
||||
click: void;
|
||||
}>();
|
||||
|
||||
const dispatchChange = createEventDispatcher<{
|
||||
change: boolean;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<button
|
||||
class="clickable-card"
|
||||
class:has-top-radius={hasTopRadius}
|
||||
class:has-bottom-radius={hasBottomRadius}
|
||||
class:has-bottom-line={hasBottomLine}
|
||||
style="padding: {padding}"
|
||||
on:click={() => {
|
||||
dispatchClick('click');
|
||||
|
||||
dispatchChange('change', checked);
|
||||
|
||||
checked = !checked;
|
||||
}}
|
||||
class:card-disabled={disabled}
|
||||
{disabled}
|
||||
>
|
||||
<div class="clickable-card__content">
|
||||
{#if SLOTS.title}
|
||||
<h3 class="text-base-15 text-bold clickable-card__title">
|
||||
<slot name="title" />
|
||||
</h3>
|
||||
{/if}
|
||||
{#if SLOTS.body}
|
||||
<p class="text-base-body-12 clickable-card__text">
|
||||
<slot name="body" />
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
{#if SLOTS.actions}
|
||||
<div class="clickable-card__actions">
|
||||
<slot name="actions" />
|
||||
</div>
|
||||
{/if}
|
||||
</button>
|
||||
|
||||
<style lang="post-css">
|
||||
.clickable-card {
|
||||
display: flex;
|
||||
gap: var(--space-16);
|
||||
border-left: 1px solid var(--clr-theme-container-outline-light);
|
||||
border-right: 1px solid var(--clr-theme-container-outline-light);
|
||||
background-color: var(--clr-theme-container-light);
|
||||
transition:
|
||||
background-color var(--transition-fast),
|
||||
border-color var(--transition-fast);
|
||||
text-align: left;
|
||||
|
||||
&:hover {
|
||||
background-color: color-mix(
|
||||
in srgb,
|
||||
var(--clr-theme-container-light),
|
||||
var(--darken-tint-extralight)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
.card-disabled {
|
||||
opacity: 0.6;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.clickable-card__content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-8);
|
||||
}
|
||||
|
||||
.clickable-card__title {
|
||||
color: var(--clr-theme-scale-ntrl-0);
|
||||
}
|
||||
|
||||
.clickable-card__text {
|
||||
color: var(--clr-theme-scale-ntrl-30);
|
||||
}
|
||||
|
||||
.clickable-card__actions {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* MODIFIERS */
|
||||
|
||||
.has-top-radius {
|
||||
border-top: 1px solid var(--clr-theme-container-outline-light);
|
||||
border-top-left-radius: var(--radius-l);
|
||||
border-top-right-radius: var(--radius-l);
|
||||
}
|
||||
|
||||
.has-bottom-radius {
|
||||
border-bottom-left-radius: var(--radius-l);
|
||||
border-bottom-right-radius: var(--radius-l);
|
||||
}
|
||||
|
||||
.has-bottom-line {
|
||||
border-bottom: 1px solid var(--clr-theme-container-outline-light);
|
||||
}
|
||||
</style>
|
@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import SectionCard from './SectionCard.svelte';
|
||||
import { getCloudApiClient, type User } from '$lib/backend/cloud';
|
||||
import ClickableCard from '$lib/components/ClickableCard.svelte';
|
||||
import Link from '$lib/components/Link.svelte';
|
||||
import Spacer from '$lib/components/Spacer.svelte';
|
||||
import Toggle from '$lib/components/Toggle.svelte';
|
||||
@ -33,7 +33,7 @@
|
||||
dispatch('updated', { ...project, api: { ...cloudProject, sync: project.api.sync } });
|
||||
});
|
||||
|
||||
const onSyncChange = async (event: CustomEvent<boolean>) => {
|
||||
async function onSyncChange(sync: boolean) {
|
||||
if (!user) return;
|
||||
try {
|
||||
const cloudProject =
|
||||
@ -43,26 +43,26 @@
|
||||
description: project.description,
|
||||
uid: project.id
|
||||
}));
|
||||
dispatch('updated', { ...project, api: { ...cloudProject, sync: event.detail } });
|
||||
dispatch('updated', { ...project, api: { ...cloudProject, sync } });
|
||||
} catch (error) {
|
||||
console.error(`Failed to update project sync status: ${error}`);
|
||||
toasts.error('Failed to update project sync status');
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const aiGenToggle = () => {
|
||||
function aiGenToggle() {
|
||||
$aiGenEnabled = !$aiGenEnabled;
|
||||
$aiGenAutoBranchNamingEnabled = $aiGenEnabled;
|
||||
};
|
||||
}
|
||||
|
||||
const aiGenBranchNamesToggle = () => {
|
||||
function aiGenBranchNamesToggle() {
|
||||
$aiGenAutoBranchNamingEnabled = !$aiGenAutoBranchNamingEnabled;
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if user}
|
||||
<div class="aigen-wrap">
|
||||
<ClickableCard on:click={aiGenToggle}>
|
||||
<SectionCard on:click={aiGenToggle} orientation="row">
|
||||
<svelte:fragment slot="title">Enable branch and commit message generation</svelte:fragment>
|
||||
<svelte:fragment slot="body">
|
||||
Uses OpenAI's API. If enabled, diffs will sent to OpenAI's servers when pressing the
|
||||
@ -71,9 +71,9 @@
|
||||
<svelte:fragment slot="actions">
|
||||
<Toggle checked={$aiGenEnabled} on:change={aiGenToggle} />
|
||||
</svelte:fragment>
|
||||
</ClickableCard>
|
||||
</SectionCard>
|
||||
|
||||
<ClickableCard disabled={!$aiGenEnabled} on:click={aiGenBranchNamesToggle}>
|
||||
<SectionCard disabled={!$aiGenEnabled} on:click={aiGenBranchNamesToggle} orientation="row">
|
||||
<svelte:fragment slot="title">Automatically generate branch names</svelte:fragment>
|
||||
<svelte:fragment slot="actions">
|
||||
<Toggle
|
||||
@ -82,7 +82,7 @@
|
||||
on:change={aiGenBranchNamesToggle}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
</ClickableCard>
|
||||
</SectionCard>
|
||||
</div>
|
||||
|
||||
<Spacer />
|
||||
@ -90,14 +90,14 @@
|
||||
{#if user.role === 'admin'}
|
||||
<h3 class="text-base-15 text-bold">Full data synchronization</h3>
|
||||
|
||||
<ClickableCard on:change={onSyncChange}>
|
||||
<SectionCard on:change={(e) => onSyncChange(e.detail)} orientation="row">
|
||||
<svelte:fragment slot="body">
|
||||
Sync my history, repository and branch data for backup, sharing and team features.
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="actions">
|
||||
<Toggle checked={project.api?.sync || false} on:change={onSyncChange} />
|
||||
<Toggle checked={project.api?.sync || false} on:change={(e) => onSyncChange(e.detail)} />
|
||||
</svelte:fragment>
|
||||
</ClickableCard>
|
||||
</SectionCard>
|
||||
|
||||
{#if project.api}
|
||||
<div class="api-link">
|
||||
|
@ -1,19 +1,17 @@
|
||||
<script lang="ts">
|
||||
import ClickableCard from './ClickableCard.svelte';
|
||||
import RadioButton from './RadioButton.svelte';
|
||||
import SectionCard from './SectionCard.svelte';
|
||||
import Spacer from './Spacer.svelte';
|
||||
import TextBox from './TextBox.svelte';
|
||||
import { invoke } from '$lib/backend/ipc';
|
||||
import Button from '$lib/components/Button.svelte';
|
||||
import Link from '$lib/components/Link.svelte';
|
||||
import { copyToClipboard } from '$lib/utils/clipboard';
|
||||
import { debounce } from '$lib/utils/debounce';
|
||||
import { openExternalUrl } from '$lib/utils/url';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import type { Key, Project } from '$lib/backend/projects';
|
||||
import { createEventDispatcher, onMount } from 'svelte';
|
||||
import type { Key, KeyType, Project } from '$lib/backend/projects';
|
||||
|
||||
export let project: Project;
|
||||
export let sshKey = '';
|
||||
|
||||
const dispatch = createEventDispatcher<{
|
||||
updated: {
|
||||
@ -21,234 +19,186 @@
|
||||
};
|
||||
}>();
|
||||
|
||||
export function get_public_key() {
|
||||
return invoke<string>('get_public_key');
|
||||
}
|
||||
|
||||
let sshKey = '';
|
||||
get_public_key().then((key) => {
|
||||
sshKey = key;
|
||||
});
|
||||
|
||||
let selectedOption =
|
||||
project.preferred_key === 'generated'
|
||||
? 'generated'
|
||||
: project.preferred_key === 'default'
|
||||
? 'default'
|
||||
: project.preferred_key === 'gitCredentialsHelper'
|
||||
? 'gitCredentialsHelper'
|
||||
: 'local';
|
||||
let selectedType: KeyType =
|
||||
typeof project.preferred_key == 'string' ? project.preferred_key : 'local';
|
||||
|
||||
let privateKeyPath =
|
||||
project.preferred_key === 'generated' ||
|
||||
project.preferred_key === 'default' ||
|
||||
project.preferred_key === 'gitCredentialsHelper'
|
||||
? ''
|
||||
: project.preferred_key.local.private_key_path;
|
||||
typeof project.preferred_key == 'string' ? '' : project.preferred_key.local.private_key_path;
|
||||
|
||||
let privateKeyPassphrase =
|
||||
project.preferred_key === 'generated' ||
|
||||
project.preferred_key === 'default' ||
|
||||
project.preferred_key === 'gitCredentialsHelper'
|
||||
? ''
|
||||
: project.preferred_key.local.passphrase;
|
||||
typeof project.preferred_key == 'string' ? '' : project.preferred_key.local.passphrase;
|
||||
|
||||
function setLocalKey() {
|
||||
if (privateKeyPath.length) {
|
||||
dispatch('updated', {
|
||||
preferred_key: {
|
||||
local: {
|
||||
private_key_path: privateKeyPath,
|
||||
passphrase:
|
||||
privateKeyPassphrase && privateKeyPassphrase.length > 0
|
||||
? privateKeyPassphrase
|
||||
: undefined
|
||||
}
|
||||
if (privateKeyPath.trim().length == 0) return;
|
||||
dispatch('updated', {
|
||||
preferred_key: {
|
||||
local: {
|
||||
private_key_path: privateKeyPath.trim(),
|
||||
passphrase: privateKeyPassphrase || undefined
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function setGitCredentialsHelperKey() {
|
||||
dispatch('updated', {
|
||||
preferred_key: 'gitCredentialsHelper'
|
||||
});
|
||||
}
|
||||
|
||||
function setDefaultKey() {
|
||||
dispatch('updated', {
|
||||
preferred_key: 'default'
|
||||
});
|
||||
}
|
||||
|
||||
function setGeneratedKey() {
|
||||
dispatch('updated', {
|
||||
preferred_key: 'generated'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let showPassphrase = false;
|
||||
|
||||
let form: HTMLFormElement;
|
||||
|
||||
function onFormChange(form: HTMLFormElement) {
|
||||
const formData = new FormData(form);
|
||||
selectedType = formData.get('credentialType') as KeyType;
|
||||
if (selectedType != 'local') {
|
||||
dispatch('updated', { preferred_key: selectedType });
|
||||
} else {
|
||||
setLocalKey();
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
form.credentialType.value = selectedType;
|
||||
});
|
||||
</script>
|
||||
|
||||
<section class="git-auth-wrap">
|
||||
<div class="git-auth-wrap">
|
||||
<h3 class="text-base-15 text-bold">Git Authentication</h3>
|
||||
<p class="text-base-body-12">
|
||||
Configure the authentication flow for GitButler when authenticating with your Git remote
|
||||
provider.
|
||||
</p>
|
||||
|
||||
<form>
|
||||
<fieldset class="git-radio">
|
||||
<ClickableCard
|
||||
hasBottomRadius={false}
|
||||
on:click={() => {
|
||||
if (selectedOption == 'default') return;
|
||||
<form class="git-radio" bind:this={form} on:change={(e) => onFormChange(e.currentTarget)}>
|
||||
<SectionCard roundedBottom={false} orientation="row" labelFor="credential-default">
|
||||
<svelte:fragment slot="title">Auto detect</svelte:fragment>
|
||||
|
||||
selectedOption = 'default';
|
||||
setDefaultKey();
|
||||
}}
|
||||
>
|
||||
<svelte:fragment slot="title">Auto detect</svelte:fragment>
|
||||
<svelte:fragment slot="actions">
|
||||
<RadioButton name="credentialType" id="credential-default" value="default" />
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="actions">
|
||||
<RadioButton bind:group={selectedOption} value="default" on:input={setDefaultKey} />
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="body">
|
||||
GitButler will attempt all available authentication flows automatically.
|
||||
</svelte:fragment>
|
||||
</SectionCard>
|
||||
|
||||
<svelte:fragment slot="body">
|
||||
GitButler will attempt all available authentication flows automatically.
|
||||
</svelte:fragment>
|
||||
</ClickableCard>
|
||||
<SectionCard
|
||||
roundedTop={false}
|
||||
roundedBottom={false}
|
||||
bottomBorder={selectedType !== 'local'}
|
||||
orientation="row"
|
||||
labelFor="credential-local"
|
||||
>
|
||||
<svelte:fragment slot="title">Use existing SSH key</svelte:fragment>
|
||||
|
||||
<ClickableCard
|
||||
hasTopRadius={false}
|
||||
hasBottomRadius={false}
|
||||
hasBottomLine={selectedOption !== 'local'}
|
||||
on:click={() => {
|
||||
selectedOption = 'local';
|
||||
}}
|
||||
>
|
||||
<svelte:fragment slot="title">Use existing SSH key</svelte:fragment>
|
||||
<svelte:fragment slot="actions">
|
||||
<RadioButton name="credentialType" id="credential-local" value="local" />
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="actions">
|
||||
<RadioButton bind:group={selectedOption} value="local" />
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="body">
|
||||
Add the path to an existing SSH key that GitButler can use.
|
||||
</svelte:fragment>
|
||||
</SectionCard>
|
||||
|
||||
<svelte:fragment slot="body">
|
||||
Add the path to an existing SSH key that GitButler can use.
|
||||
</svelte:fragment>
|
||||
</ClickableCard>
|
||||
|
||||
{#if selectedOption === 'local'}
|
||||
<SectionCard hasTopRadius={false} hasBottomRadius={false}>
|
||||
<div class="inputs-group">
|
||||
<TextBox
|
||||
label="Path to private key"
|
||||
placeholder="for example: ~/.ssh/id_rsa"
|
||||
bind:value={privateKeyPath}
|
||||
on:input={debounce(setLocalKey, 600)}
|
||||
/>
|
||||
|
||||
<div class="input-with-button">
|
||||
<TextBox
|
||||
label="Passphrase (optional)"
|
||||
type={showPassphrase ? 'text' : 'password'}
|
||||
bind:value={privateKeyPassphrase}
|
||||
on:input={debounce(setLocalKey, 600)}
|
||||
wide
|
||||
/>
|
||||
<Button
|
||||
size="large"
|
||||
color="neutral"
|
||||
kind="outlined"
|
||||
icon={showPassphrase ? 'eye-shown' : 'eye-hidden'}
|
||||
on:click={() => (showPassphrase = !showPassphrase)}
|
||||
width={150}
|
||||
>
|
||||
{showPassphrase ? 'Hide passphrase' : 'Show passphrase'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</SectionCard>
|
||||
{/if}
|
||||
|
||||
<ClickableCard
|
||||
hasTopRadius={false}
|
||||
hasBottomRadius={false}
|
||||
hasBottomLine={selectedOption !== 'generated'}
|
||||
on:click={() => {
|
||||
if (selectedOption == 'generated') return;
|
||||
|
||||
selectedOption = 'generated';
|
||||
setGeneratedKey();
|
||||
}}
|
||||
>
|
||||
<svelte:fragment slot="title">Use locally generated SSH key</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="actions">
|
||||
<RadioButton bind:group={selectedOption} value="generated" on:input={setGeneratedKey} />
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="body">
|
||||
GitButler will use a locally generated SSH key. For this to work you need to add the
|
||||
following public key to your Git remote provider:
|
||||
</svelte:fragment>
|
||||
</ClickableCard>
|
||||
|
||||
{#if selectedOption === 'generated'}
|
||||
<SectionCard hasTopRadius={false} hasBottomRadius={false}>
|
||||
<TextBox readonly selectall bind:value={sshKey} />
|
||||
<div class="row-buttons">
|
||||
<Button
|
||||
kind="filled"
|
||||
color="primary"
|
||||
icon="copy"
|
||||
on:mousedown={() => copyToClipboard(sshKey)}
|
||||
>
|
||||
Copy to Clipboard
|
||||
</Button>
|
||||
<Button
|
||||
kind="outlined"
|
||||
color="neutral"
|
||||
icon="open-link"
|
||||
on:mousedown={() => {
|
||||
openExternalUrl('https://github.com/settings/ssh/new');
|
||||
}}
|
||||
>
|
||||
Add key to GitHub
|
||||
</Button>
|
||||
</div>
|
||||
</SectionCard>
|
||||
{/if}
|
||||
|
||||
<ClickableCard
|
||||
hasTopRadius={false}
|
||||
on:click={() => {
|
||||
if (selectedOption == 'gitCredentialsHelper') return;
|
||||
|
||||
selectedOption = 'gitCredentialsHelper';
|
||||
setGitCredentialsHelperKey();
|
||||
}}
|
||||
>
|
||||
<svelte:fragment slot="title">Use a Git credentials helper</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="body">
|
||||
GitButler will use the system's git credentials helper.
|
||||
<Link target="_blank" rel="noreferrer" href="https://git-scm.com/doc/credential-helpers">
|
||||
Learn more
|
||||
</Link>
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="actions">
|
||||
<RadioButton
|
||||
bind:group={selectedOption}
|
||||
value="gitCredentialsHelper"
|
||||
on:input={setGitCredentialsHelperKey}
|
||||
{#if selectedType == 'local'}
|
||||
<SectionCard hasTopRadius={false} hasBottomRadius={false} orientation="row">
|
||||
<div class="inputs-group">
|
||||
<TextBox
|
||||
label="Path to private key"
|
||||
placeholder="for example: ~/.ssh/id_rsa"
|
||||
bind:value={privateKeyPath}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
</ClickableCard>
|
||||
</fieldset>
|
||||
|
||||
<div class="input-with-button">
|
||||
<TextBox
|
||||
label="Passphrase (optional)"
|
||||
type={showPassphrase ? 'text' : 'password'}
|
||||
bind:value={privateKeyPassphrase}
|
||||
wide
|
||||
/>
|
||||
<Button
|
||||
size="large"
|
||||
color="neutral"
|
||||
kind="outlined"
|
||||
icon={showPassphrase ? 'eye-shown' : 'eye-hidden'}
|
||||
on:click={() => (showPassphrase = !showPassphrase)}
|
||||
width={150}
|
||||
>
|
||||
{showPassphrase ? 'Hide passphrase' : 'Show passphrase'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</SectionCard>
|
||||
{/if}
|
||||
|
||||
<SectionCard
|
||||
roundedTop={false}
|
||||
roundedBottom={false}
|
||||
bottomBorder={selectedType !== 'generated'}
|
||||
orientation="row"
|
||||
labelFor="credential-generated"
|
||||
>
|
||||
<svelte:fragment slot="title">Use locally generated SSH key</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="actions">
|
||||
<RadioButton name="credentialType" id="credential-generated" value="generated" />
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="body">
|
||||
GitButler will use a locally generated SSH key. For this to work you need to add the
|
||||
following public key to your Git remote provider:
|
||||
</svelte:fragment>
|
||||
</SectionCard>
|
||||
|
||||
{#if selectedType === 'generated'}
|
||||
<SectionCard hasTopRadius={false} hasBottomRadius={false} orientation="row">
|
||||
<TextBox readonly selectall bind:value={sshKey} />
|
||||
<div class="row-buttons">
|
||||
<Button
|
||||
kind="filled"
|
||||
color="primary"
|
||||
icon="copy"
|
||||
on:mousedown={() => copyToClipboard(sshKey)}
|
||||
>
|
||||
Copy to Clipboard
|
||||
</Button>
|
||||
<Button
|
||||
kind="outlined"
|
||||
color="neutral"
|
||||
icon="open-link"
|
||||
on:mousedown={() => {
|
||||
openExternalUrl('https://github.com/settings/ssh/new');
|
||||
}}
|
||||
>
|
||||
Add key to GitHub
|
||||
</Button>
|
||||
</div>
|
||||
</SectionCard>
|
||||
{/if}
|
||||
|
||||
<SectionCard
|
||||
roundedTop={false}
|
||||
roundedBottom={false}
|
||||
orientation="row"
|
||||
labelFor="credential-helper"
|
||||
>
|
||||
<svelte:fragment slot="title">Use a Git credentials helper</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="body">
|
||||
GitButler will use the system's git credentials helper.
|
||||
<Link target="_blank" rel="noreferrer" href="https://git-scm.com/doc/credential-helpers">
|
||||
Learn more
|
||||
</Link>
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="actions">
|
||||
<RadioButton name="credentialType" value="gitCredentialsHelper" id="credential-helper" />
|
||||
</svelte:fragment>
|
||||
</SectionCard>
|
||||
<SectionCard roundedTop={false} orientation="row">
|
||||
<svelte:fragment slot="body">
|
||||
<Button wide>Test credentials</Button>
|
||||
</svelte:fragment>
|
||||
</SectionCard>
|
||||
</form>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<Spacer />
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import SectionCard from './SectionCard.svelte';
|
||||
import Spacer from './Spacer.svelte';
|
||||
import ClickableCard from '$lib/components/ClickableCard.svelte';
|
||||
import Toggle from '$lib/components/Toggle.svelte';
|
||||
import { projectRunCommitHooks } from '$lib/config/config';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
@ -18,62 +18,48 @@
|
||||
omit_certificate_check?: boolean;
|
||||
};
|
||||
}>();
|
||||
|
||||
const onAllowForcePushingChange = () => {
|
||||
dispatch('updated', { ok_with_force_push: allowForcePushing });
|
||||
};
|
||||
|
||||
const onOmitCertificateCheckChange = () => {
|
||||
dispatch('updated', { omit_certificate_check: omitCertificateCheck });
|
||||
};
|
||||
|
||||
const onRunCommitHooksChange = () => {
|
||||
$runCommitHooks = !$runCommitHooks;
|
||||
};
|
||||
</script>
|
||||
|
||||
<section class="wrapper">
|
||||
<ClickableCard
|
||||
on:click={() => {
|
||||
allowForcePushing = !allowForcePushing;
|
||||
onAllowForcePushingChange();
|
||||
}}
|
||||
>
|
||||
<SectionCard orientation="row" labelFor="allowForcePush">
|
||||
<svelte:fragment slot="title">Allow force pushing</svelte:fragment>
|
||||
<svelte:fragment slot="body">
|
||||
Force pushing allows GitButler to override branches even if they were pushed to remote. We
|
||||
will never force push to the trunk.
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="actions">
|
||||
<Toggle bind:checked={allowForcePushing} on:change={onAllowForcePushingChange} />
|
||||
<Toggle
|
||||
id="allowForcePush"
|
||||
bind:checked={allowForcePushing}
|
||||
on:change={() => dispatch('updated', { ok_with_force_push: allowForcePushing })}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
</ClickableCard>
|
||||
</SectionCard>
|
||||
|
||||
<ClickableCard
|
||||
on:click={() => {
|
||||
omitCertificateCheck = !omitCertificateCheck;
|
||||
onOmitCertificateCheckChange();
|
||||
}}
|
||||
>
|
||||
<SectionCard orientation="row" labelFor="omitCertificateCheck">
|
||||
<svelte:fragment slot="title">Ignore host certificate checks</svelte:fragment>
|
||||
<svelte:fragment slot="body">
|
||||
Enabling this will ignore host certificate checks when authenticating with ssh.
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="actions">
|
||||
<Toggle bind:checked={omitCertificateCheck} on:change={onOmitCertificateCheckChange} />
|
||||
<Toggle
|
||||
id="omitCertificateCheck"
|
||||
bind:checked={omitCertificateCheck}
|
||||
on:change={() => dispatch('updated', { omit_certificate_check: omitCertificateCheck })}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
</ClickableCard>
|
||||
</SectionCard>
|
||||
|
||||
<ClickableCard on:click={onRunCommitHooksChange}>
|
||||
<SectionCard labelFor="runHooks" orientation="row">
|
||||
<svelte:fragment slot="title">Run commit hooks</svelte:fragment>
|
||||
<svelte:fragment slot="body">
|
||||
Enabling this will run any git pre and post commit hooks you have configured in your
|
||||
repository.
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="actions">
|
||||
<Toggle bind:checked={$runCommitHooks} on:change={onRunCommitHooksChange} />
|
||||
<Toggle id="runHooks" bind:checked={$runCommitHooks} />
|
||||
</svelte:fragment>
|
||||
</ClickableCard>
|
||||
</SectionCard>
|
||||
</section>
|
||||
|
||||
<Spacer />
|
||||
|
@ -3,8 +3,8 @@
|
||||
|
||||
export let small = false;
|
||||
export let disabled = false;
|
||||
export let group = '';
|
||||
export let value = '';
|
||||
export let id = '';
|
||||
</script>
|
||||
|
||||
<input
|
||||
@ -14,10 +14,10 @@
|
||||
type="radio"
|
||||
class="radio"
|
||||
class:small
|
||||
{id}
|
||||
{value}
|
||||
{name}
|
||||
{disabled}
|
||||
bind:group
|
||||
/>
|
||||
|
||||
<style lang="postcss">
|
||||
|
@ -1,18 +1,32 @@
|
||||
<script lang="ts" context="module">
|
||||
export type SectionCardBackground = 'loading' | 'success' | 'error' | undefined;
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export let orientation: 'row' | 'column' = 'column';
|
||||
export let hasTopRadius = true;
|
||||
export let hasBottomRadius = true;
|
||||
export let hasBottomLine = true;
|
||||
export let extraPadding = false;
|
||||
export let roundedTop = true;
|
||||
export let roundedBottom = true;
|
||||
export let bottomBorder = true;
|
||||
export let background: SectionCardBackground = undefined;
|
||||
export let noBorder = false;
|
||||
export let labelFor = '';
|
||||
|
||||
const SLOTS = $$props.$$slots;
|
||||
</script>
|
||||
|
||||
<section
|
||||
<label
|
||||
for={labelFor}
|
||||
class="section-card"
|
||||
style:flex-direction={orientation}
|
||||
class:has-top-radius={hasTopRadius}
|
||||
class:has-bottom-radius={hasBottomRadius}
|
||||
class:has-bottom-line={hasBottomLine}
|
||||
class:extra-padding={extraPadding}
|
||||
class:rounded-top={roundedTop}
|
||||
class:rounded-bottom={roundedBottom}
|
||||
class:bottom-border={bottomBorder}
|
||||
class:no-border={noBorder}
|
||||
class:loading={background == 'loading'}
|
||||
class:success={background == 'success'}
|
||||
class:error={background == 'error'}
|
||||
>
|
||||
{#if SLOTS.iconSide}
|
||||
<div class="section-card__icon-side">
|
||||
@ -35,7 +49,12 @@
|
||||
</div>
|
||||
{/if}
|
||||
<slot />
|
||||
</section>
|
||||
{#if SLOTS.actions}
|
||||
<div class="clickable-card__actions">
|
||||
<slot name="actions" />
|
||||
</div>
|
||||
{/if}
|
||||
</label>
|
||||
|
||||
<style lang="post-css">
|
||||
.section-card {
|
||||
@ -45,17 +64,34 @@
|
||||
border-left: 1px solid var(--clr-theme-container-outline-light);
|
||||
border-right: 1px solid var(--clr-theme-container-outline-light);
|
||||
background-color: var(--clr-theme-container-light);
|
||||
cursor: pointer;
|
||||
transition:
|
||||
background-color var(--transition-fast),
|
||||
border-color var(--transition-fast);
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.loading {
|
||||
background: var(--clr-theme-container-pale);
|
||||
}
|
||||
|
||||
.success {
|
||||
background: var(--clr-theme-pop-container);
|
||||
}
|
||||
|
||||
.error {
|
||||
background: var(--clr-theme-warn-container);
|
||||
}
|
||||
.extra-padding {
|
||||
padding: var(--space-20);
|
||||
}
|
||||
|
||||
.section-card__content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-8);
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
.section-card__title {
|
||||
@ -68,20 +104,22 @@
|
||||
|
||||
/* MODIFIERS */
|
||||
|
||||
.has-top-radius {
|
||||
.rounded-top {
|
||||
border-top: 1px solid var(--clr-theme-container-outline-light);
|
||||
border-top-left-radius: var(--radius-m);
|
||||
border-top-right-radius: var(--radius-m);
|
||||
}
|
||||
|
||||
.has-bottom-radius {
|
||||
.rounded-bottom {
|
||||
border-bottom-left-radius: var(--radius-m);
|
||||
border-bottom-right-radius: var(--radius-m);
|
||||
}
|
||||
|
||||
.has-bottom-line {
|
||||
.bottom-border {
|
||||
border-bottom: 1px solid var(--clr-theme-container-outline-light);
|
||||
user-select: text;
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.no-border {
|
||||
border: none;
|
||||
}
|
||||
</style>
|
||||
|
@ -9,6 +9,7 @@
|
||||
export let checked = false;
|
||||
export let value = '';
|
||||
export let help = '';
|
||||
export let id = '';
|
||||
|
||||
let input: HTMLInputElement;
|
||||
const dispatch = createEventDispatcher<{ change: boolean }>();
|
||||
@ -26,7 +27,7 @@
|
||||
class:small
|
||||
{value}
|
||||
{name}
|
||||
id={name}
|
||||
{id}
|
||||
{disabled}
|
||||
use:tooltip={help}
|
||||
/>
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { initPostHog } from '$lib/analytics/posthog';
|
||||
import { initSentry } from '$lib/analytics/sentry';
|
||||
import { AuthService } from '$lib/backend/auth';
|
||||
import { getCloudApiClient } from '$lib/backend/cloud';
|
||||
import { ProjectService } from '$lib/backend/projects';
|
||||
import { UpdaterService } from '$lib/backend/updater';
|
||||
@ -33,7 +34,6 @@ export const load: LayoutLoad = async ({ fetch: realFetch }: { fetch: typeof fet
|
||||
if (enabled) initPostHog();
|
||||
});
|
||||
const userService = new UserService();
|
||||
const updaterService = new UpdaterService();
|
||||
|
||||
// TODO: Find a workaround to avoid this dynamic import
|
||||
// https://github.com/sveltejs/kit/issues/905
|
||||
@ -41,9 +41,10 @@ export const load: LayoutLoad = async ({ fetch: realFetch }: { fetch: typeof fet
|
||||
const defaultPath = await homeDir();
|
||||
|
||||
return {
|
||||
authService: new AuthService(),
|
||||
projectService: new ProjectService(defaultPath),
|
||||
cloud: getCloudApiClient({ fetch: realFetch }),
|
||||
updaterService,
|
||||
updaterService: new UpdaterService(),
|
||||
userService,
|
||||
user$: userService.user$
|
||||
};
|
||||
|
@ -11,7 +11,7 @@ import type { LayoutLoad } from './$types';
|
||||
export const prerender = false;
|
||||
|
||||
export const load: LayoutLoad = async ({ params, parent }) => {
|
||||
const { user$, projectService, userService } = await parent();
|
||||
const { authService, projectService, userService } = await parent();
|
||||
const projectId = params.projectId;
|
||||
const project$ = projectService.getProject(projectId);
|
||||
const fetches$ = getFetchNotifications(projectId);
|
||||
@ -41,8 +41,11 @@ export const load: LayoutLoad = async ({ params, parent }) => {
|
||||
branchController
|
||||
);
|
||||
|
||||
const user$ = userService.user$;
|
||||
|
||||
return {
|
||||
projectId,
|
||||
authService,
|
||||
branchController,
|
||||
baseBranchService,
|
||||
githubService,
|
||||
|
@ -8,7 +8,6 @@
|
||||
import SectionCard from '$lib/components/SectionCard.svelte';
|
||||
import ContentWrapper from '$lib/components/settings/ContentWrapper.svelte';
|
||||
import * as toasts from '$lib/utils/toasts';
|
||||
import type { UserError } from '$lib/backend/ipc';
|
||||
import type { Key, Project } from '$lib/backend/projects';
|
||||
import type { PageData } from './$types';
|
||||
import { goto } from '$app/navigation';
|
||||
@ -24,34 +23,41 @@
|
||||
let deleteConfirmationModal: RemoveProjectButton;
|
||||
let isDeleting = false;
|
||||
|
||||
const onDeleteClicked = () =>
|
||||
Promise.resolve()
|
||||
.then(() => (isDeleting = true))
|
||||
.then(() => projectService.deleteProject($project$?.id))
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
toasts.error('Failed to delete project');
|
||||
})
|
||||
.then(() => toasts.success('Project deleted'))
|
||||
.then(() => goto('/'))
|
||||
.finally(() => {
|
||||
isDeleting = false;
|
||||
projectService.reload();
|
||||
});
|
||||
async function onDeleteClicked() {
|
||||
isDeleting = true;
|
||||
try {
|
||||
projectService.deleteProject($project$?.id);
|
||||
toasts.success('Project deleted');
|
||||
goto('/');
|
||||
} catch (err: any) {
|
||||
console.error(err);
|
||||
toasts.error('Failed to delete project');
|
||||
} finally {
|
||||
isDeleting = false;
|
||||
projectService.reload();
|
||||
}
|
||||
}
|
||||
|
||||
const onKeysUpdated = (e: { detail: { preferred_key: Key } }) =>
|
||||
projectService
|
||||
.updateProject({ ...$project$, ...e.detail })
|
||||
.then(() => toasts.success('Preferred key updated'))
|
||||
.catch((e: UserError) => {
|
||||
toasts.error(e.message);
|
||||
});
|
||||
const onCloudUpdated = (e: { detail: Project }) =>
|
||||
async function onKeysUpdated(e: { detail: { preferred_key: Key } }) {
|
||||
try {
|
||||
projectService.updateProject({ ...$project$, ...e.detail });
|
||||
toasts.success('Preferred key updated');
|
||||
} catch (err: any) {
|
||||
toasts.error(err.message);
|
||||
}
|
||||
}
|
||||
|
||||
async function onCloudUpdated(e: { detail: Project }) {
|
||||
projectService.updateProject({ ...$project$, ...e.detail });
|
||||
const onPreferencesUpdated = (e: {
|
||||
}
|
||||
|
||||
async function onPreferencesUpdated(e: {
|
||||
detail: { ok_with_force_push?: boolean; omit_certificate_check?: boolean };
|
||||
}) => projectService.updateProject({ ...$project$, ...e.detail });
|
||||
const onDetailsUpdated = async (e: { detail: Project }) => {
|
||||
}) {
|
||||
await projectService.updateProject({ ...$project$, ...e.detail });
|
||||
}
|
||||
|
||||
async function onDetailsUpdated(e: { detail: Project }) {
|
||||
const api =
|
||||
$user$ && e.detail.api
|
||||
? await cloud.projects.update($user$?.access_token, e.detail.api.repository_id, {
|
||||
@ -59,12 +65,11 @@
|
||||
description: e.detail.description
|
||||
})
|
||||
: undefined;
|
||||
|
||||
projectService.updateProject({
|
||||
...e.detail,
|
||||
api: api ? { ...api, sync: e.detail.api?.sync || false } : undefined
|
||||
});
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if !$project$}
|
||||
|
@ -2,7 +2,6 @@
|
||||
import { deleteAllData } from '$lib/backend/data';
|
||||
import AnalyticsSettings from '$lib/components/AnalyticsSettings.svelte';
|
||||
import Button from '$lib/components/Button.svelte';
|
||||
import ClickableCard from '$lib/components/ClickableCard.svelte';
|
||||
import GithubIntegration from '$lib/components/GithubIntegration.svelte';
|
||||
import Link from '$lib/components/Link.svelte';
|
||||
import Login from '$lib/components/Login.svelte';
|
||||
@ -19,37 +18,33 @@
|
||||
import * as toasts from '$lib/utils/toasts';
|
||||
import { openExternalUrl } from '$lib/utils/url';
|
||||
import { invoke } from '@tauri-apps/api/tauri';
|
||||
import { onMount } from 'svelte';
|
||||
import type { PageData } from './$types';
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
export let data: PageData;
|
||||
const { cloud, user$, userService } = data;
|
||||
|
||||
$: saving = false;
|
||||
|
||||
$: userPicture = $user$?.picture;
|
||||
|
||||
const { cloud, user$, userService, authService } = data;
|
||||
const fileTypes = ['image/jpeg', 'image/png'];
|
||||
|
||||
const validFileType = (file: File) => {
|
||||
return fileTypes.includes(file.type);
|
||||
};
|
||||
|
||||
const onPictureChange = (e: Event) => {
|
||||
const target = e.target as HTMLInputElement;
|
||||
const file = target.files?.[0];
|
||||
|
||||
if (file && validFileType(file)) {
|
||||
userPicture = URL.createObjectURL(file);
|
||||
} else {
|
||||
userPicture = $user$?.picture;
|
||||
toasts.error('Please use a valid image file');
|
||||
}
|
||||
};
|
||||
// TODO: Maybe break these into components?
|
||||
let currentSection: 'profile' | 'git-stuff' | 'telemetry' | 'integrations' = 'profile';
|
||||
|
||||
// TODO: Refactor such that this variable isn't needed
|
||||
let newName = '';
|
||||
|
||||
let loaded = false;
|
||||
let isDeleting = false;
|
||||
|
||||
let signCommits = false;
|
||||
let annotateCommits = true;
|
||||
let sshKey = '';
|
||||
|
||||
let deleteConfirmationModal: Modal;
|
||||
|
||||
$: saving = false;
|
||||
$: userPicture = $user$?.picture;
|
||||
|
||||
$: if ($user$ && !loaded) {
|
||||
loaded = true;
|
||||
cloud.user.get($user$?.access_token).then((cloudUser) => {
|
||||
@ -59,7 +54,19 @@
|
||||
newName = $user$?.name || '';
|
||||
}
|
||||
|
||||
const onSubmit = async (e: SubmitEvent) => {
|
||||
function onPictureChange(e: Event) {
|
||||
const target = e.target as HTMLInputElement;
|
||||
const file = target.files?.[0];
|
||||
|
||||
if (file && fileTypes.includes(file.type)) {
|
||||
userPicture = URL.createObjectURL(file);
|
||||
} else {
|
||||
userPicture = $user$?.picture;
|
||||
toasts.error('Please use a valid image file');
|
||||
}
|
||||
}
|
||||
|
||||
async function onSubmit(e: SubmitEvent) {
|
||||
if (!$user$) return;
|
||||
saving = true;
|
||||
|
||||
@ -80,74 +87,55 @@
|
||||
toasts.error('Failed to update user');
|
||||
}
|
||||
saving = false;
|
||||
};
|
||||
}
|
||||
|
||||
let isDeleting = false;
|
||||
let deleteConfirmationModal: Modal;
|
||||
|
||||
export function git_get_config(params: { key: string }) {
|
||||
// TODO: These kinds of functions should be implemented on an injected service
|
||||
function gitGetConfig(params: { key: string }) {
|
||||
return invoke<string>('git_get_global_config', params);
|
||||
}
|
||||
|
||||
export function git_set_config(params: { key: string; value: string }) {
|
||||
function gitSetConfig(params: { key: string; value: string }) {
|
||||
return invoke<string>('git_set_global_config', params);
|
||||
}
|
||||
|
||||
const setCommitterSetting = (value: boolean) => {
|
||||
annotateCommits = value;
|
||||
git_set_config({
|
||||
function toggleCommitterSigning() {
|
||||
annotateCommits = !annotateCommits;
|
||||
gitSetConfig({
|
||||
key: 'gitbutler.gitbutlerCommitter',
|
||||
value: value ? '1' : '0'
|
||||
value: annotateCommits ? '1' : '0'
|
||||
});
|
||||
};
|
||||
|
||||
const setSigningSetting = (value: boolean) => {
|
||||
signCommits = value;
|
||||
git_set_config({
|
||||
key: 'gitbutler.signCommits',
|
||||
value: value ? 'true' : 'false'
|
||||
});
|
||||
};
|
||||
|
||||
export function get_public_key() {
|
||||
return invoke<string>('get_public_key');
|
||||
}
|
||||
|
||||
let sshKey = '';
|
||||
get_public_key().then((key) => {
|
||||
sshKey = key;
|
||||
});
|
||||
function toggleSigningSetting() {
|
||||
signCommits = !signCommits;
|
||||
gitSetConfig({
|
||||
key: 'gitbutler.signCommits',
|
||||
value: signCommits ? 'true' : 'false'
|
||||
});
|
||||
}
|
||||
|
||||
$: annotateCommits = true;
|
||||
$: signCommits = false;
|
||||
|
||||
git_get_config({ key: 'gitbutler.gitbutlerCommitter' }).then((value) => {
|
||||
annotateCommits = value ? value === '1' : false;
|
||||
});
|
||||
|
||||
git_get_config({ key: 'gitbutler.signCommits' }).then((value) => {
|
||||
signCommits = value ? value === 'true' : false;
|
||||
});
|
||||
|
||||
const onDeleteClicked = () =>
|
||||
Promise.resolve()
|
||||
.then(() => (isDeleting = true))
|
||||
.then(() => deleteAllData())
|
||||
async function onDeleteClicked() {
|
||||
isDeleting = true;
|
||||
try {
|
||||
deleteAllData();
|
||||
await userService.logout();
|
||||
// TODO: Delete user from observable!!!
|
||||
.then(() => userService.logout())
|
||||
.then(() => toasts.success('All data deleted'))
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
toasts.error('Failed to delete project');
|
||||
})
|
||||
.then(() => deleteConfirmationModal.close())
|
||||
.then(() => goto('/', { replaceState: true, invalidateAll: true }))
|
||||
.finally(() => (isDeleting = false));
|
||||
toasts.success('All data deleted');
|
||||
goto('/', { replaceState: true, invalidateAll: true });
|
||||
} catch (err: any) {
|
||||
console.error(err);
|
||||
toasts.error('Failed to delete project');
|
||||
} finally {
|
||||
deleteConfirmationModal.close();
|
||||
isDeleting = false;
|
||||
}
|
||||
}
|
||||
|
||||
let currentSection: 'profile' | 'git-stuff' | 'telemetry' | 'integrations' = 'profile';
|
||||
|
||||
const toggleGBCommiter = () => setCommitterSetting(!annotateCommits);
|
||||
const toggleGBSigner = () => setSigningSetting(!signCommits);
|
||||
onMount(async () => {
|
||||
sshKey = await authService.getPublicKey();
|
||||
annotateCommits = (await gitGetConfig({ key: 'gitbutler.gitbutlerCommitter' })) == '1';
|
||||
signCommits = (await gitGetConfig({ key: 'gitbutler.signCommits' })) == 'true';
|
||||
});
|
||||
</script>
|
||||
|
||||
<section class="profile-page">
|
||||
@ -233,7 +221,7 @@
|
||||
</ContentWrapper>
|
||||
{:else if currentSection === 'git-stuff'}
|
||||
<ContentWrapper title="Git stuff">
|
||||
<ClickableCard on:click={toggleGBCommiter}>
|
||||
<SectionCard labelFor="committerSigning" orientation="row">
|
||||
<svelte:fragment slot="title">Credit GitButler as the Committer</svelte:fragment>
|
||||
<svelte:fragment slot="body">
|
||||
By default, everything in the GitButler client is free to use. You can opt in to crediting
|
||||
@ -247,9 +235,13 @@
|
||||
</Link>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="actions">
|
||||
<Toggle checked={annotateCommits} on:change={toggleGBCommiter} />
|
||||
<Toggle
|
||||
id="commiterSigning"
|
||||
checked={annotateCommits}
|
||||
on:change={toggleCommitterSigning}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
</ClickableCard>
|
||||
</SectionCard>
|
||||
|
||||
<Spacer />
|
||||
|
||||
@ -283,7 +275,7 @@
|
||||
</div>
|
||||
</SectionCard>
|
||||
|
||||
<ClickableCard on:click={toggleGBSigner}>
|
||||
<SectionCard labelFor="signingSetting" orientation="row">
|
||||
<svelte:fragment slot="title">Sign Commits with the above SSH Key</svelte:fragment>
|
||||
<svelte:fragment slot="body">
|
||||
If you want GitButler to sign your commits with the SSH key we generated, then you can add
|
||||
@ -297,9 +289,9 @@
|
||||
</Link>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="actions">
|
||||
<Toggle checked={signCommits} on:change={toggleGBSigner} />
|
||||
<Toggle id="signingSetting" checked={signCommits} on:change={toggleSigningSetting} />
|
||||
</svelte:fragment>
|
||||
</ClickableCard>
|
||||
</SectionCard>
|
||||
</ContentWrapper>
|
||||
{:else if currentSection === 'telemetry'}
|
||||
<ContentWrapper title="Telemetry">
|
||||
|
14
gitbutler-ui/static/images/lock.svg
Normal file
14
gitbutler-ui/static/images/lock.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<svg width="20" height="21" viewBox="0 0 20 21" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M15 14.5V7.5C15 4.73858 12.7614 2.5 10 2.5V2.5C7.23858 2.5 5 4.73858 5 7.5V14.5" stroke="#67C2BE" stroke-width="2"/>
|
||||
<rect x="1" y="8.5" width="18" height="11" rx="2" fill="url(#paint0_linear_1756_54140)"/>
|
||||
<rect x="1" y="8.5" width="18" height="11" rx="2" fill="#67C2BE"/>
|
||||
<path d="M7 14C7 14.8284 6.32843 15.5 5.5 15.5C4.67157 15.5 4 14.8284 4 14C4 13.1716 4.67157 12.5 5.5 12.5C6.32843 12.5 7 13.1716 7 14Z" fill="black"/>
|
||||
<path d="M11.5 14C11.5 14.8284 10.8284 15.5 10 15.5C9.17157 15.5 8.5 14.8284 8.5 14C8.5 13.1716 9.17157 12.5 10 12.5C10.8284 12.5 11.5 13.1716 11.5 14Z" fill="black"/>
|
||||
<path d="M16 14C16 14.8284 15.3284 15.5 14.5 15.5C13.6716 15.5 13 14.8284 13 14C13 13.1716 13.6716 12.5 14.5 12.5C15.3284 12.5 16 13.1716 16 14Z" fill="black"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_1756_54140" x1="10" y1="8.5" x2="-0.60513" y2="33.6738" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FFBA52"/>
|
||||
<stop offset="1" stop-color="#B8873E"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
Loading…
Reference in New Issue
Block a user