Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stack Uploading #5930

Merged
merged 2 commits into from
Jan 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 9 additions & 15 deletions apps/desktop/src/lib/backend/projectCloudSync.svelte.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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;
Expand Down
20 changes: 1 addition & 19 deletions apps/desktop/src/lib/branch/SeriesHeader.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -377,7 +370,7 @@
{/if}
</div>
</div>
{#if ($prService && !hasNoCommits) || showCreateCloudBranch}
{#if $prService && !hasNoCommits}
<div class="branch-action">
<div class="branch-action__line" style:--bg-color={lineColor}></div>
<div class="branch-action__body">
Expand Down Expand Up @@ -417,17 +410,6 @@
</Button>
{/if}
{/if}

{#if showCreateCloudBranch}
<Button
style="ghost"
outline
disabled={branch.patches.length === 0}
onclick={() => {
cloudBranchCreationService.createBranch(stack.id);
}}>Publish Branch</Button
>
{/if}
</div>
</div>
{/if}
Expand Down
28 changes: 0 additions & 28 deletions apps/desktop/src/lib/branch/cloudBranchCreationService.ts

This file was deleted.

51 changes: 51 additions & 0 deletions apps/desktop/src/lib/history/stackPublishingService.ts
Original file line number Diff line number Diff line change
@@ -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<boolean>;

#joinedUserAndProject: Readable<{
user: User | undefined;
project: Project | undefined;
}>;

constructor(
private readonly commandService: CommandService,
private readonly user: Readable<User | undefined>,
private readonly project: Readable<Project | undefined>
) {
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<void> {
// 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<string>('push_stack_to_review', {
projectId: project!.id,
user: user!,
stackId
});
}

private canTakeSnapshotGivenUserAndProject(user: User | undefined, project: Project | undefined) {
return user !== undefined && !!project?.api?.sync;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -141,40 +141,42 @@

<Loading loadable={cloudProject}>
{#snippet children(cloudProject)}
<Section>
{#snippet title()}
Link your project with an organization
{/snippet}

<RegisterInterest interest={usersOrganizationsInterest} />

<div>
{#each usersOrganizations as loadableOrganization, index}
<SectionCard
roundedBottom={index === usersOrganizations.length - 1}
roundedTop={index === 0}
orientation="row"
centerAlign
>
{#snippet children()}
<Loading loadable={loadableOrganization}>
{#snippet children(organization)}
<h5 class="text-15 text-bold flex-grow">
{organization.name || organization.slug}
</h5>
{/snippet}
</Loading>
{/snippet}
{#snippet actions()}
<ProjectConnectModal
organizationSlug={loadableOrganization.id}
projectRepositoryId={cloudProject.repositoryId}
/>
{/snippet}
</SectionCard>
{/each}
</div>
</Section>
{#if !cloudProject.parentProjectRepositoryId}
<Section>
{#snippet title()}
Link your project with an organization
{/snippet}

<RegisterInterest interest={usersOrganizationsInterest} />

<div>
{#each usersOrganizations as loadableOrganization, index}
<SectionCard
roundedBottom={index === usersOrganizations.length - 1}
roundedTop={index === 0}
orientation="row"
centerAlign
>
{#snippet children()}
<Loading loadable={loadableOrganization}>
{#snippet children(organization)}
<h5 class="text-15 text-bold flex-grow">
{organization.name || organization.slug}
</h5>
{/snippet}
</Loading>
{/snippet}
{#snippet actions()}
<ProjectConnectModal
organizationSlug={loadableOrganization.id}
projectRepositoryId={cloudProject.repositoryId}
/>
{/snippet}
</SectionCard>
{/each}
</div>
</Section>
{/if}
{/snippet}
</Loading>
{:else if !$project?.api?.repository_id}
Expand Down
24 changes: 20 additions & 4 deletions apps/desktop/src/lib/stack/header/StackHeader.svelte
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
<script lang="ts">
import HeaderControlSection from './HeaderControlSection.svelte';
import HeaderMetaSection from './HeaderMetaSection.svelte';
import { cloudReviewFunctionality } from '$lib/config/uiFeatureFlags';
import { StackPublishingService } from '$lib/history/stackPublishingService';
import { BranchController } from '$lib/vbranches/branchController';
import { BranchStack } from '$lib/vbranches/types';
import { getContext } from '@gitbutler/shared/context';
import Button from '@gitbutler/ui/Button.svelte';
import { isError } from '@gitbutler/ui/utils/typeguards';

interface Props {
Expand All @@ -13,25 +16,38 @@

const branchController = getContext(BranchController);

const { onCollapseButtonClick, stack: branch }: Props = $props();
const { onCollapseButtonClick, stack }: Props = $props();

const nonArchivedSeries = $derived(
branch.series.filter((s) => {
stack.series.filter((s) => {
if (isError(s)) return s;
return !s.archived;
})
);

const stackPublishingService = getContext(StackPublishingService);
const canPublish = stackPublishingService.canPublish;
let publishing = $state<'inert' | 'loading' | 'complete'>('inert');

async function publishStack() {
publishing = 'loading';
await stackPublishingService.upsertStack(stack.id);
publishing = 'complete';
}
</script>

<div class="stack-header">
<HeaderControlSection
isDefault={branch.selectedForChanges}
isDefault={stack.selectedForChanges}
{onCollapseButtonClick}
onDefaultSet={async () => {
await branchController.setSelectedForChanges(branch.id);
await branchController.setSelectedForChanges(stack.id);
}}
/>
<HeaderMetaSection series={nonArchivedSeries} {onCollapseButtonClick} />
{#if $cloudReviewFunctionality && $canPublish}
<Button onclick={publishStack} loading={publishing === 'loading'}>Publish stack</Button>
{/if}
</div>

<style lang="postcss">
Expand Down
4 changes: 2 additions & 2 deletions apps/desktop/src/routes/[projectId]/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import ProjectSettingsMenuAction from '$lib/barmenuActions/ProjectSettingsMenuAction.svelte';
import { BaseBranch, NoDefaultTarget } from '$lib/baseBranch/baseBranch';
import { BaseBranchService } from '$lib/baseBranch/baseBranchService';
import { CloudBranchCreationService } from '$lib/branch/cloudBranchCreationService';
import { BranchListingService, CombinedBranchListingService } from '$lib/branches/branchListing';
import { BranchDragActionsFactory } from '$lib/branches/dragActions';
import { GitBranchService } from '$lib/branches/gitBranch';
Expand All @@ -27,6 +26,7 @@
import { createForgeRepoServiceStore } from '$lib/forge/interface/forgeRepoService';
import History from '$lib/history/History.svelte';
import { HistoryService } from '$lib/history/history';
import { StackPublishingService } from '$lib/history/stackPublishingService';
import { SyncedSnapshotService } from '$lib/history/syncedSnapshotService';
import { ModeService } from '$lib/modes/service';
import Navigation from '$lib/navigation/Navigation.svelte';
Expand Down Expand Up @@ -91,7 +91,7 @@

// Cloud related services
setContext(SyncedSnapshotService, data.syncedSnapshotService);
setContext(CloudBranchCreationService, data.cloudBranchCreationService);
setContext(StackPublishingService, data.stackPublishingService);
setContext(FileService, data.fileService);
});

Expand Down
Loading
Loading