Can remove repository if it's missing

This commit is contained in:
estib 2024-09-02 14:43:23 +02:00
parent 6530284a80
commit be89f303d2
7 changed files with 160 additions and 0 deletions

View File

@ -100,6 +100,16 @@ export class ProjectService {
return await invoke('delete_project', { id });
}
async deleteProjectByPath(path: string): Promise<boolean> {
try {
await invoke('delete_project_by_path', { path });
return true;
} catch (error) {
// Will only fail if no project matches the path given
return false;
}
}
async promptForDirectory(): Promise<string | undefined> {
const selectedPath = open({ directory: true, recursive: true, defaultPath: this.homeDir });
if (selectedPath) {

View File

@ -0,0 +1,19 @@
<script lang="ts">
import InexistentRepo from './errorBoundaryActions/InexistentRepo.svelte';
import { getKnownError, KnownErrorType } from '$lib/utils/errors';
interface Props {
error: unknown;
}
const { error }: Props = $props();
let knownError = $derived(getKnownError(error));
</script>
{#if knownError}
<div>
{#if knownError.type === KnownErrorType.FailedToOpenRepoInexistent}
<InexistentRepo error={knownError} />
{/if}
</div>
{/if}

View File

@ -1,5 +1,6 @@
<script lang="ts">
import DecorativeSplitView from './DecorativeSplitView.svelte';
import ErrorBoundaryActions from './ErrorBoundaryActions.svelte';
import ProjectSwitcher from './ProjectSwitcher.svelte';
import loadErrorSvg from '$lib/assets/illustrations/load-error.svg?raw';
import Icon from '@gitbutler/ui/Icon.svelte';
@ -18,6 +19,8 @@
{error ? error : 'An unknown error occurred'}
</div>
<ErrorBoundaryActions {error} />
<div class="problem__switcher">
<ProjectSwitcher />
</div>

View File

@ -0,0 +1,60 @@
<script lang="ts">
import { ProjectService } from '$lib/backend/projects';
import { getContext } from '$lib/utils/context';
import Button from '@gitbutler/ui/Button.svelte';
import type { FailedToOpenRepoInexistentError } from '$lib/utils/errors';
const projectService = getContext(ProjectService);
interface Props {
error: FailedToOpenRepoInexistentError;
}
const { error }: Props = $props();
let deleteSucceeded: boolean | undefined = $state(undefined);
async function stopTracking() {
deleteSucceeded = await projectService.deleteProjectByPath(error.path);
}
</script>
<div class="container">
{#if deleteSucceeded === undefined}
<p class="text-12 text-body text-bold">
Do you want to stop tracking the repo under this path?:
</p>
<p class="text-12 text-body repo-path">{error.path}</p>
<div class="button-container">
<Button style="error" onclick={stopTracking}>Remove</Button>
</div>
{/if}
{#if deleteSucceeded === true}
<p class="text-12 text-body text-bold">
Repo removed successfully
</p>
{/if}
{#if deleteSucceeded === false}
<p class="text-12 text-body text-bold">
Failed to remove repo
</p>
{/if}
</div>
<style lang="postcss">
.container {
display: flex;
flex-direction: column;
gap: 12px;
}
.repo-path {
text-align: center;
}
.button-container {
display: flex;
justify-content: center;
}
</style>

View File

@ -0,0 +1,51 @@
const FAILED_TO_OPEN_REPO_INEXISTENT_PATTERN =
/^Could not open repository at '(.+)' as it does not exist$/;
export enum KnownErrorType {
FailedToOpenRepoInexistent = 'FailedToOpenRepoInexistent'
}
interface BaseKnownError {
type: KnownErrorType;
}
export interface FailedToOpenRepoInexistentError extends BaseKnownError {
type: KnownErrorType.FailedToOpenRepoInexistent;
path: string;
}
export type KnownError = FailedToOpenRepoInexistentError;
export function getErrorMessage(something: unknown): string | null {
if (something instanceof Error) return something.message;
if (typeof something === 'string') return something;
return null;
}
export function getErrorType(something: unknown): KnownErrorType | null {
const message = getErrorMessage(something);
if (!message) return null;
if (FAILED_TO_OPEN_REPO_INEXISTENT_PATTERN.test(message)) {
return KnownErrorType.FailedToOpenRepoInexistent;
}
return null;
}
export function getKnownError(something: unknown): KnownError | null {
const type = getErrorType(something);
if (!type) return null;
const message = getErrorMessage(something);
if (!message) return null;
switch (type) {
case KnownErrorType.FailedToOpenRepoInexistent: {
const match = message.match(FAILED_TO_OPEN_REPO_INEXISTENT_PATTERN);
if (!match) return null;
return {
type,
path: match[1] ?? ''
};
}
}
}

View File

@ -146,6 +146,7 @@ fn main() {
projects::commands::get_project,
projects::commands::update_project,
projects::commands::delete_project,
projects::commands::delete_project_by_path,
projects::commands::list_projects,
projects::commands::set_project_active,
projects::commands::open_project_in_window,

View File

@ -91,6 +91,22 @@ pub mod commands {
pub fn delete_project(projects: State<'_, Controller>, id: ProjectId) -> Result<(), Error> {
projects.delete(id).map_err(Into::into)
}
#[tauri::command(async)]
#[instrument(skip(projects), err(Debug))]
pub fn delete_project_by_path(
projects: State<'_, Controller>,
path: &path::Path,
) -> Result<(), Error> {
let project = projects
.list()
.context("failed to list projects")?
.into_iter()
.find(|project| project.path == path)
.ok_or_else(|| anyhow::anyhow!("project not found"))?;
projects.delete(project.id).map_err(Into::into)
}
}
#[derive(serde::Deserialize, serde::Serialize)]