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