diff --git a/Cargo.lock b/Cargo.lock index 36e2b166b5..a737b326d0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2760,6 +2760,7 @@ version = "0.0.0" dependencies = [ "bstr", "git2", + "gitbutler-oxidize", "gix", "serde", ] @@ -2791,6 +2792,7 @@ dependencies = [ "gitbutler-fs", "gitbutler-git", "gitbutler-id", + "gitbutler-oxidize", "gitbutler-reference", "gitbutler-repo", "gitbutler-repo-actions", @@ -2804,6 +2806,7 @@ dependencies = [ "tempfile", "toml 0.8.19", "tracing", + "uuid", ] [[package]] @@ -2824,11 +2827,13 @@ dependencies = [ "gitbutler-error", "gitbutler-id", "gitbutler-oplog", + "gitbutler-oxidize", "gitbutler-project", "gitbutler-reference", "gitbutler-stack", "gitbutler-url", "gitbutler-user", + "gix", "itertools 0.14.0", "tracing", ] diff --git a/apps/desktop/src/lib/backend/projectCloudSync.svelte.ts b/apps/desktop/src/lib/backend/projectCloudSync.svelte.ts index 044a61df2b..38b35ee2a1 100644 --- a/apps/desktop/src/lib/backend/projectCloudSync.svelte.ts +++ b/apps/desktop/src/lib/backend/projectCloudSync.svelte.ts @@ -1,5 +1,4 @@ -import { registerInterest } from '@gitbutler/shared/interest/registerInterestFunction.svelte'; -import { projectsSelectors } from '@gitbutler/shared/organizations/projectsSlice'; +import { getProjectByRepositoryId } from '@gitbutler/shared/organizations/projectsPreview.svelte'; import { readableToReactive } from '@gitbutler/shared/reactiveUtils.svelte'; import type { ProjectService, ProjectsService } from '$lib/backend/projects'; import type { HttpClient } from '@gitbutler/shared/network/httpClient'; @@ -16,26 +15,21 @@ export function projectCloudSync( const project = readableToReactive(projectService.project); const authentictionAvailable = readableToReactive(httpClient.authenticationAvailable); - $effect(() => { - if (!project.current?.api || !authentictionAvailable) return; - - const cloudProjectInterest = cloudProjectService.getProjectInterest( - project.current.api.repository_id - ); - registerInterest(cloudProjectInterest); - }); - const loadableCloudProject = $derived( - project.current?.api - ? projectsSelectors.selectById(appState.projects, project.current.api.repository_id) + project.current?.api && authentictionAvailable + ? getProjectByRepositoryId(appState, cloudProjectService, project.current.api.repository_id) : undefined ); $effect(() => { - if (!project.current?.api || !loadableCloudProject || loadableCloudProject.status !== 'found') + if ( + !project.current?.api || + !loadableCloudProject?.current || + loadableCloudProject?.current.status !== 'found' + ) return; - const cloudProject = loadableCloudProject.value; + const cloudProject = loadableCloudProject.current.value; const persistedProjectUpdatedAt = new Date(project.current.api.updated_at).getTime(); const cloudProjectUpdatedAt = new Date(cloudProject.updatedAt).getTime(); if (persistedProjectUpdatedAt >= cloudProjectUpdatedAt) return; diff --git a/apps/desktop/src/lib/branch/SeriesHeader.svelte b/apps/desktop/src/lib/branch/SeriesHeader.svelte index e7ff547138..32f890612d 100644 --- a/apps/desktop/src/lib/branch/SeriesHeader.svelte +++ b/apps/desktop/src/lib/branch/SeriesHeader.svelte @@ -10,10 +10,8 @@ import { Project } from '$lib/backend/projects'; import { BaseBranch } from '$lib/baseBranch/baseBranch'; import SeriesHeaderContextMenu from '$lib/branch/SeriesHeaderContextMenu.svelte'; - import { CloudBranchCreationService } from '$lib/branch/cloudBranchCreationService'; import ContextMenu from '$lib/components/contextmenu/ContextMenu.svelte'; import { projectAiGenEnabled } from '$lib/config/config'; - import { cloudReviewFunctionality } from '$lib/config/uiFeatureFlags'; import { getForge } from '$lib/forge/interface/forge'; import { getForgeListingService } from '$lib/forge/interface/forgeListingService'; import { getForgePrService } from '$lib/forge/interface/forgePrService'; @@ -145,11 +143,6 @@ await Promise.allSettled([prMonitor?.refresh(), checksMonitor?.update()]); } - const cloudBranchCreationService = getContext(CloudBranchCreationService); - const showCreateCloudBranch = $derived( - $cloudReviewFunctionality && cloudBranchCreationService.canCreateBranch - ); - /** * We are starting to store pull request id's locally so if we find one that does not have * one locally stored then we set it once. @@ -377,7 +370,7 @@ {/if} - {#if ($prService && !hasNoCommits) || showCreateCloudBranch} + {#if $prService && !hasNoCommits}
@@ -417,17 +410,6 @@ {/if} {/if} - - {#if showCreateCloudBranch} - - {/if}
{/if} diff --git a/apps/desktop/src/lib/branch/cloudBranchCreationService.ts b/apps/desktop/src/lib/branch/cloudBranchCreationService.ts deleted file mode 100644 index 8a552a0fc6..0000000000 --- a/apps/desktop/src/lib/branch/cloudBranchCreationService.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { derived, type Readable } from 'svelte/store'; -import type { SyncedSnapshotService } from '$lib/history/syncedSnapshotService'; - -/** - * This service is responsible for integrating the client side oplog - * manipulation with actual cloud patch stack creation. - */ -export class CloudBranchCreationService { - canCreateBranch: Readable; - - constructor( - private readonly syncedSnapshotService: SyncedSnapshotService - // private readonly cloudBranchesService: CloudBranchesService - ) { - this.canCreateBranch = derived( - [this.syncedSnapshotService.canTakeSnapshot], - ([canTakeSnapshot]) => { - return canTakeSnapshot; - } - ); - } - - async createBranch(_branchId: string): Promise { - const _oplogSha = await this.syncedSnapshotService.takeSyncedSnapshot(); - // const cloudBranch = await this.cloudBranchesService.createBranch(branchId, oplogSha); - // return cloudBranch; - } -} diff --git a/apps/desktop/src/lib/history/stackPublishingService.ts b/apps/desktop/src/lib/history/stackPublishingService.ts new file mode 100644 index 0000000000..00c9b7ae46 --- /dev/null +++ b/apps/desktop/src/lib/history/stackPublishingService.ts @@ -0,0 +1,51 @@ +import { CommandService } from '$lib/backend/ipc'; +import { derived, get, type Readable } from 'svelte/store'; +import type { Project } from '$lib/backend/projects'; +import type { User } from '$lib/stores/user'; + +export class StackPublishingService { + /** + * Signal for the frontend to choose whether or not to provide the + * takeSyncedSnapshot button. + */ + canPublish: Readable; + + #joinedUserAndProject: Readable<{ + user: User | undefined; + project: Project | undefined; + }>; + + constructor( + private readonly commandService: CommandService, + private readonly user: Readable, + private readonly project: Readable + ) { + this.#joinedUserAndProject = derived([this.user, this.project], ([user, project]) => { + return { user, project }; + }); + + this.canPublish = derived(this.#joinedUserAndProject, ({ user, project }) => { + return this.canTakeSnapshotGivenUserAndProject(user, project); + }); + } + + async upsertStack(stackId?: string): Promise { + // Take a snapshot + const { user, project } = get(this.#joinedUserAndProject); + + // Project and user are now defined + if (!this.canTakeSnapshotGivenUserAndProject(user, project)) { + throw new Error('Cannot publish branch'); + } + + await this.commandService.invoke('push_stack_to_review', { + projectId: project!.id, + user: user!, + stackId + }); + } + + private canTakeSnapshotGivenUserAndProject(user: User | undefined, project: Project | undefined) { + return user !== undefined && !!project?.api?.sync; + } +} diff --git a/apps/desktop/src/lib/settings/userPreferences/CloudProjectSettings.svelte b/apps/desktop/src/lib/settings/userPreferences/CloudProjectSettings.svelte index c7094b547a..e6ab14b163 100644 --- a/apps/desktop/src/lib/settings/userPreferences/CloudProjectSettings.svelte +++ b/apps/desktop/src/lib/settings/userPreferences/CloudProjectSettings.svelte @@ -141,40 +141,42 @@ {#snippet children(cloudProject)} -
- {#snippet title()} - Link your project with an organization - {/snippet} - - - -
- {#each usersOrganizations as loadableOrganization, index} - - {#snippet children()} - - {#snippet children(organization)} -
- {organization.name || organization.slug} -
- {/snippet} -
- {/snippet} - {#snippet actions()} - - {/snippet} -
- {/each} -
-
+ {#if !cloudProject.parentProjectRepositoryId} +
+ {#snippet title()} + Link your project with an organization + {/snippet} + + + +
+ {#each usersOrganizations as loadableOrganization, index} + + {#snippet children()} + + {#snippet children(organization)} +
+ {organization.name || organization.slug} +
+ {/snippet} +
+ {/snippet} + {#snippet actions()} + + {/snippet} +
+ {/each} +
+
+ {/if} {/snippet}
{:else if !$project?.api?.repository_id} diff --git a/apps/desktop/src/lib/stack/header/StackHeader.svelte b/apps/desktop/src/lib/stack/header/StackHeader.svelte index 1cfbe61517..f41877a201 100644 --- a/apps/desktop/src/lib/stack/header/StackHeader.svelte +++ b/apps/desktop/src/lib/stack/header/StackHeader.svelte @@ -1,9 +1,12 @@
{ - await branchController.setSelectedForChanges(branch.id); + await branchController.setSelectedForChanges(stack.id); }} /> + {#if $cloudReviewFunctionality && $canPublish} + + {/if}