mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-12-23 17:43:47 +03:00
global project stores
This commit is contained in:
parent
4afa457baa
commit
1f1455019c
@ -1,6 +1,5 @@
|
||||
import { invoke } from '$lib/ipc';
|
||||
import type { Project as CloudProject } from '$lib/api/cloud';
|
||||
import { asyncWritable, derived } from '@square/svelte-store';
|
||||
|
||||
export type Project = {
|
||||
id: string;
|
||||
@ -22,31 +21,5 @@ export const update = (params: {
|
||||
|
||||
export const add = (params: { path: string }) => invoke<Project>('add_project', params);
|
||||
|
||||
export const del = (params: { id: string }) => invoke('delete_project', params);
|
||||
|
||||
const store = asyncWritable([], list);
|
||||
|
||||
export const Project = ({ id }: { id: string }) => ({
|
||||
...derived(store, (projects) => projects?.find((p) => p.id === id)),
|
||||
update: (params: Partial<Pick<Project, 'title' | 'description' | 'api'>>) =>
|
||||
update({
|
||||
project: {
|
||||
id,
|
||||
...params
|
||||
}
|
||||
}).then((project) => {
|
||||
store.update((projects) => projects.map((p) => (p.id === project.id ? project : p)));
|
||||
return project;
|
||||
}),
|
||||
delete: () =>
|
||||
del({ id }).then(() => store.update((projects) => projects.filter((p) => p.id !== id)))
|
||||
});
|
||||
|
||||
export const Projects = () => ({
|
||||
...store,
|
||||
add: (params: { path: string }) =>
|
||||
add(params).then((project) => {
|
||||
store.update((projects) => [...projects, project]);
|
||||
return project;
|
||||
})
|
||||
});
|
||||
const del = (params: { id: string }) => invoke('delete_project', params);
|
||||
export { del as delete };
|
||||
|
@ -1 +1,3 @@
|
||||
export { default as user } from './user';
|
||||
export { default as projects } from './projects';
|
||||
export { default as project } from './project';
|
||||
|
24
src/lib/stores/project.ts
Normal file
24
src/lib/stores/project.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { api } from '$lib';
|
||||
import { derived } from '@square/svelte-store';
|
||||
import type { Project } from '$lib/api';
|
||||
import projects from './projects';
|
||||
|
||||
export default ({ id }: { id: string }) => ({
|
||||
...derived(projects, (projects) => projects?.find((p) => p.id === id)),
|
||||
update: (params: Partial<Pick<Project, 'title' | 'description' | 'api'>>) =>
|
||||
api.projects
|
||||
.update({
|
||||
project: {
|
||||
id,
|
||||
...params
|
||||
}
|
||||
})
|
||||
.then((project) => {
|
||||
projects.update((projects) => projects.map((p) => (p.id === project.id ? project : p)));
|
||||
return project;
|
||||
}),
|
||||
delete: () =>
|
||||
api.projects
|
||||
.delete({ id })
|
||||
.then(() => projects.update((projects) => projects.filter((p) => p.id !== id)))
|
||||
});
|
13
src/lib/stores/projects.ts
Normal file
13
src/lib/stores/projects.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { api } from '$lib';
|
||||
import { asyncWritable } from '@square/svelte-store';
|
||||
|
||||
const projects = asyncWritable([], api.projects.list);
|
||||
|
||||
export default {
|
||||
...projects,
|
||||
add: (params: { path: string }) =>
|
||||
api.projects.add(params).then((project) => {
|
||||
projects.update((projects) => [...projects, project]);
|
||||
return project;
|
||||
})
|
||||
};
|
@ -6,7 +6,6 @@
|
||||
import type { LayoutData } from './$types';
|
||||
import { Link, CommandPalette } from '$lib/components';
|
||||
import { page } from '$app/stores';
|
||||
import { derived } from '@square/svelte-store';
|
||||
import { onMount } from 'svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import { unsubscribe } from '$lib/utils';
|
||||
@ -16,13 +15,12 @@
|
||||
import ShareIssueModal from './ShareIssueModal.svelte';
|
||||
|
||||
export let data: LayoutData;
|
||||
const { posthog, projects, sentry, cloud } = data;
|
||||
const { posthog, sentry, cloud } = data;
|
||||
|
||||
const projects = stores.projects;
|
||||
const user = stores.user;
|
||||
|
||||
const project = derived([page, projects], ([page, projects]) =>
|
||||
projects?.find((project) => project.id === page.params.projectId)
|
||||
);
|
||||
$: project = stores.project({ id: $page.params.projectId });
|
||||
|
||||
let commandPalette: CommandPalette;
|
||||
let linkProjectModal: LinkProjectModal;
|
||||
@ -98,7 +96,7 @@
|
||||
<CommandPalette bind:this={commandPalette} {projects} {project} />
|
||||
{/await}
|
||||
|
||||
<LinkProjectModal bind:this={linkProjectModal} {cloud} {projects} />
|
||||
<LinkProjectModal bind:this={linkProjectModal} {cloud} />
|
||||
|
||||
<ShareIssueModal bind:this={shareIssueModal} user={$user} {cloud} />
|
||||
<ShareIssueModal bind:this={shareIssueModal} {cloud} />
|
||||
</div>
|
||||
|
@ -11,7 +11,6 @@ export const csr = true;
|
||||
export const load: LayoutLoad = wrapLoadWithSentry(({ fetch }) => {
|
||||
log.setup();
|
||||
return {
|
||||
projects: api.projects.Projects(),
|
||||
cloud: api.CloudApi({ fetch }),
|
||||
posthog: Posthog(),
|
||||
sentry: Sentry()
|
||||
|
@ -1,11 +1,8 @@
|
||||
<script lang="ts">
|
||||
import type { LayoutData } from './$types';
|
||||
import { Button, Tooltip } from '$lib/components';
|
||||
import { events } from '$lib';
|
||||
import { events, stores } from '$lib';
|
||||
|
||||
export let data: LayoutData;
|
||||
|
||||
const { projects } = data;
|
||||
const projects = stores.projects;
|
||||
</script>
|
||||
|
||||
<div class="h-full w-full p-8">
|
||||
|
@ -6,10 +6,10 @@
|
||||
import { IconFolder, IconLoading } from '$lib/icons';
|
||||
import { toasts, api, stores } from '$lib';
|
||||
|
||||
export let projects: ReturnType<typeof api.projects.Projects>;
|
||||
export let cloud: ReturnType<typeof api.CloudApi>;
|
||||
|
||||
const user = stores.user;
|
||||
const projects = stores.projects;
|
||||
|
||||
const cloudProjects = asyncDerived(user, async (user) =>
|
||||
user ? await cloud.projects.list(user.access_token) : []
|
||||
@ -17,10 +17,10 @@
|
||||
|
||||
let selectedRepositoryId: string | null = null;
|
||||
|
||||
let project: ReturnType<typeof api.projects.Project> | undefined;
|
||||
let project: ReturnType<typeof stores.project> | undefined;
|
||||
|
||||
export const show = async (id: string) => {
|
||||
project = api.projects.Project({ id });
|
||||
project = stores.project({ id });
|
||||
modal.show();
|
||||
};
|
||||
|
||||
|
@ -1,14 +1,14 @@
|
||||
<script lang="ts">
|
||||
import { toasts, api } from '$lib';
|
||||
import { toasts, api, stores } from '$lib';
|
||||
import { Button, Checkbox, Modal } from '$lib/components';
|
||||
import { page } from '$app/stores';
|
||||
import type { User } from '$lib/api';
|
||||
|
||||
export let user: User | null;
|
||||
export let cloud: ReturnType<typeof api.CloudApi>;
|
||||
|
||||
export const show = () => modal.show();
|
||||
|
||||
const user = stores.user;
|
||||
|
||||
let modal: Modal;
|
||||
|
||||
let comments = '';
|
||||
@ -53,8 +53,8 @@
|
||||
])
|
||||
)
|
||||
.then(async ([logs, data, repo]) =>
|
||||
cloud.feedback.create(user?.access_token, {
|
||||
email: user?.email ?? email,
|
||||
cloud.feedback.create($user?.access_token, {
|
||||
email: $user?.email ?? email,
|
||||
message: comments,
|
||||
logs,
|
||||
data,
|
||||
|
@ -12,8 +12,9 @@
|
||||
import { events, hotkeys, stores } from '$lib';
|
||||
|
||||
export let data: LayoutData;
|
||||
const { cloud, project, head, statuses, diffs } = data;
|
||||
const { cloud, head, statuses, diffs } = data;
|
||||
|
||||
$: project = stores.project({ id: $page.params.projectId });
|
||||
const user = stores.user;
|
||||
|
||||
let query: string;
|
||||
@ -31,12 +32,12 @@
|
||||
events.on('openQuickCommitModal', () => quickCommitModal?.show()),
|
||||
|
||||
hotkeys.on('C', () => events.emit('openQuickCommitModal')),
|
||||
hotkeys.on('Meta+Shift+C', () => goto(`/projects/${$project.id}/commit/`)),
|
||||
hotkeys.on('Meta+T', () => goto(`/projects/${$project.id}/terminal/`)),
|
||||
hotkeys.on('Meta+P', () => goto(`/projects/${$project.id}/`)),
|
||||
hotkeys.on('Meta+Shift+,', () => goto(`/projects/${$project.id}/settings/`)),
|
||||
hotkeys.on('Meta+R', () => goto(`/projects/${$project.id}/player/`)),
|
||||
hotkeys.on('a i p', () => goto(`/projects/${$project.id}/aiplayground/`))
|
||||
hotkeys.on('Meta+Shift+C', () => $project && goto(`/projects/${$project.id}/commit/`)),
|
||||
hotkeys.on('Meta+T', () => $project && goto(`/projects/${$project.id}/terminal/`)),
|
||||
hotkeys.on('Meta+P', () => $project && goto(`/projects/${$project.id}/`)),
|
||||
hotkeys.on('Meta+Shift+,', () => $project && goto(`/projects/${$project.id}/settings/`)),
|
||||
hotkeys.on('Meta+R', () => $project && goto(`/projects/${$project.id}/player/`)),
|
||||
hotkeys.on('a i p', () => $project && goto(`/projects/${$project.id}/aiplayground/`))
|
||||
)
|
||||
);
|
||||
</script>
|
||||
@ -87,7 +88,7 @@
|
||||
<li>
|
||||
<Tooltip label="Terminal">
|
||||
<Button
|
||||
on:click={() => goto(`/projects/${$project.id}/terminal`)}
|
||||
on:click={() => $project && goto(`/projects/${$project.id}/terminal`)}
|
||||
kind="plain"
|
||||
icon={IconTerminal}
|
||||
/>
|
||||
@ -96,7 +97,7 @@
|
||||
<li>
|
||||
<Tooltip label="Project settings">
|
||||
<Button
|
||||
on:click={() => goto(`/projects/${$project.id}/settings`)}
|
||||
on:click={() => $project && goto(`/projects/${$project.id}/settings`)}
|
||||
kind="plain"
|
||||
icon={IconSettings}
|
||||
/>
|
||||
@ -147,7 +148,7 @@
|
||||
</div>
|
||||
|
||||
{#await Promise.all([diffs.load(), user.load(), project.load(), statuses.load()]) then}
|
||||
{#if $user}
|
||||
{#if $user && $project}
|
||||
<QuickCommitModal
|
||||
bind:this={quickCommitModal}
|
||||
user={$user}
|
||||
|
@ -1,19 +1,13 @@
|
||||
import { api } from '$lib';
|
||||
import { error } from '@sveltejs/kit';
|
||||
import type { LayoutLoad } from './$types';
|
||||
import type { Loadable } from '@square/svelte-store';
|
||||
import type { Project } from '$lib/api';
|
||||
|
||||
export const prerender = false;
|
||||
|
||||
export const load: LayoutLoad = async ({ params }) => {
|
||||
const project = api.projects.Project({ id: params.projectId });
|
||||
if ((await project.load()) === undefined) throw error(404, new Error('Project not found'));
|
||||
return {
|
||||
head: api.git.heads.Head({ projectId: params.projectId }),
|
||||
statuses: api.git.statuses.Statuses({ projectId: params.projectId }),
|
||||
sessions: api.sessions.Sessions({ projectId: params.projectId }),
|
||||
diffs: api.git.diffs.Diffs({ projectId: params.projectId }),
|
||||
project: project as Loadable<Project> & Pick<typeof project, 'update' | 'delete'>
|
||||
diffs: api.git.diffs.Diffs({ projectId: params.projectId })
|
||||
};
|
||||
};
|
||||
|
@ -6,10 +6,12 @@
|
||||
import FileSummaries from './FileSummaries.svelte';
|
||||
import { Button, Statuses, Tooltip } from '$lib/components';
|
||||
import { goto } from '$app/navigation';
|
||||
import { stores } from '$lib';
|
||||
import { page } from '$app/stores';
|
||||
|
||||
export let data: PageData;
|
||||
$: activity = derived(data.activity, (activity) => activity);
|
||||
$: project = derived(data.project, (project) => project);
|
||||
$: project = stores.project({ id: $page.params.projectId });
|
||||
$: statuses = derived(data.statuses, (statuses) => statuses);
|
||||
$: sessions = derived(data.sessions, (sessions) => sessions);
|
||||
$: head = derived(data.head, (head) => head);
|
||||
|
@ -15,8 +15,9 @@
|
||||
import { unsubscribe } from '$lib/utils';
|
||||
|
||||
export let data: PageData;
|
||||
let { statuses, diffs, cloud, project } = data;
|
||||
let { statuses, diffs, cloud } = data;
|
||||
|
||||
$: project = stores.project({ id: $page.params.projectId });
|
||||
const user = stores.user;
|
||||
|
||||
let fullContext = false;
|
||||
|
@ -1,5 +1,4 @@
|
||||
<script lang="ts">
|
||||
import type { PageData } from './$types';
|
||||
import { IconChevronLeft, IconChevronRight, IconLoading } from '$lib/icons';
|
||||
import { files, deltas, searchResults, type SearchResult } from '$lib/api';
|
||||
import { asyncDerived } from '@square/svelte-store';
|
||||
@ -8,9 +7,9 @@
|
||||
import { page } from '$app/stores';
|
||||
import { derived } from '@square/svelte-store';
|
||||
import { goto } from '$app/navigation';
|
||||
import { stores } from '$lib';
|
||||
|
||||
export let data: PageData;
|
||||
const { project } = data;
|
||||
$: project = stores.project({ id: $page.params.projectId });
|
||||
|
||||
const limit = 10;
|
||||
const query = derived(page, (page) => page.url.searchParams.get('q'));
|
||||
|
@ -7,19 +7,22 @@
|
||||
import CloudForm from './CloudForm.svelte';
|
||||
import DetailsForm from './DetailsForm.svelte';
|
||||
import type { Project } from '$lib/api';
|
||||
import { page } from '$app/stores';
|
||||
|
||||
export let data: PageData;
|
||||
const { projects, project, cloud } = data;
|
||||
const { cloud } = data;
|
||||
|
||||
const projects = stores.projects;
|
||||
const user = stores.user;
|
||||
$: project = stores.project({ id: $page.params.projectId });
|
||||
|
||||
let deleteConfirmationModal: Modal;
|
||||
let isDeleting = false;
|
||||
|
||||
const onDeleteClicked = () =>
|
||||
const onDeleteClicked = () => {
|
||||
Promise.resolve()
|
||||
.then(() => (isDeleting = true))
|
||||
.then(() => api.projects.del({ id: $project?.id }))
|
||||
.then(() => $project && api.projects.delete({ id: $project.id }))
|
||||
.then(() => deleteConfirmationModal.close())
|
||||
.catch((e) => {
|
||||
log.error(e);
|
||||
@ -29,6 +32,7 @@
|
||||
.then(() => projects.update((projects) => projects.filter((p) => p.id !== $project?.id)))
|
||||
.then(() => toasts.success('Project deleted'))
|
||||
.finally(() => (isDeleting = false));
|
||||
};
|
||||
|
||||
const onCloudUpdated = (e: { detail: Project }) => project.update({ ...e.detail });
|
||||
const onDetailsUpdated = async (e: { detail: Project }) => {
|
||||
@ -58,8 +62,10 @@
|
||||
</div>
|
||||
<hr class="border-zinc-600" />
|
||||
{#await project.load() then}
|
||||
<CloudForm project={$project} on:updated={onCloudUpdated} />
|
||||
<DetailsForm project={$project} on:updated={onDetailsUpdated} />
|
||||
{#if $project}
|
||||
<CloudForm project={$project} on:updated={onCloudUpdated} />
|
||||
<DetailsForm project={$project} on:updated={onDetailsUpdated} />
|
||||
{/if}
|
||||
{/await}
|
||||
|
||||
<hr class="border-zinc-600" />
|
||||
@ -104,16 +110,20 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Modal bind:this={deleteConfirmationModal} title="Delete {$project.title}?">
|
||||
<p>
|
||||
Are you sure you want to delete the project,
|
||||
<span class="font-bold text-white">{$project.title}</span>? This can’t be undone.
|
||||
</p>
|
||||
{#await project.load() then}
|
||||
{#if $project}
|
||||
<Modal bind:this={deleteConfirmationModal} title="Delete {$project.title}?">
|
||||
<p>
|
||||
Are you sure you want to delete the project,
|
||||
<span class="font-bold text-white">{$project.title}</span>? This can’t be undone.
|
||||
</p>
|
||||
|
||||
<svelte:fragment slot="controls" let:close>
|
||||
<Button kind="outlined" on:click={close}>Cancel</Button>
|
||||
<Button color="destructive" loading={isDeleting} on:click={onDeleteClicked}>
|
||||
Delete project
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
</Modal>
|
||||
<svelte:fragment slot="controls" let:close>
|
||||
<Button kind="outlined" on:click={close}>Cancel</Button>
|
||||
<Button color="destructive" loading={isDeleting} on:click={onDeleteClicked}>
|
||||
Delete project
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
</Modal>
|
||||
{/if}
|
||||
{/await}
|
||||
|
@ -5,10 +5,14 @@
|
||||
import 'xterm/css/xterm.css';
|
||||
import type { Project } from '$lib/api';
|
||||
import { debounce } from '$lib/utils';
|
||||
import { page } from '$app/stores';
|
||||
import { Button, Statuses } from '$lib/components';
|
||||
import { stores } from '$lib';
|
||||
|
||||
export let data: LayoutData;
|
||||
const { project, statuses } = data;
|
||||
const { statuses } = data;
|
||||
|
||||
$: project = stores.project({ id: $page.params.projectId });
|
||||
|
||||
type Unpromisify<T> = T extends Promise<infer U> ? U : T;
|
||||
let term: Unpromisify<ReturnType<typeof setupTerminal>> | undefined;
|
||||
@ -47,8 +51,12 @@
|
||||
|
||||
<div class="main-content-container h-full">
|
||||
<div class="flex h-full w-full flex-row">
|
||||
<div class="h-full w-full" use:terminal={{ project: $project }} />
|
||||
<ResizeObserver on:resize={handleTerminalResize} />
|
||||
{#await project.load() then}
|
||||
{#if $project}
|
||||
<div class="h-full w-full" use:terminal={{ project: $project }} />
|
||||
<ResizeObserver on:resize={handleTerminalResize} />
|
||||
{/if}
|
||||
{/await}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user