From 7bf3b76e0610466abd24edbfefd35fea831d2b79 Mon Sep 17 00:00:00 2001 From: Caleb Owens Date: Thu, 31 Oct 2024 12:13:48 +0000 Subject: [PATCH] Reordering and reviewing --- .../patches/[changeId]/+layout.svelte | 3 + .../patches/[changeId]/+layout.svelte | 3 + .../cloud/patches/CloudPatchDetails.svelte | 16 ++++- .../cloud/patches/CloudPatchSections.svelte | 18 +++-- .../src/lib/cloud/patches/DiffSection.svelte | 9 ++- .../shared/src/lib/cloud/patches/sections.ts | 69 +++++++++++++++++++ .../shared/src/lib/cloud/patches/service.ts | 57 +++++++++++++++ packages/shared/src/lib/cloud/types.ts | 11 +-- 8 files changed, 171 insertions(+), 15 deletions(-) create mode 100644 packages/shared/src/lib/cloud/patches/sections.ts diff --git a/apps/desktop/src/routes/[projectId]/series/branches/[cloudBranchId]/patches/[changeId]/+layout.svelte b/apps/desktop/src/routes/[projectId]/series/branches/[cloudBranchId]/patches/[changeId]/+layout.svelte index 655e2b2c2a..9964fd97b8 100644 --- a/apps/desktop/src/routes/[projectId]/series/branches/[cloudBranchId]/patches/[changeId]/+layout.svelte +++ b/apps/desktop/src/routes/[projectId]/series/branches/[cloudBranchId]/patches/[changeId]/+layout.svelte @@ -1,4 +1,5 @@ {@render children()} diff --git a/apps/web/src/routes/repositories/[repositoryId]/branches/[cloudBranchId]/patches/[changeId]/+layout.svelte b/apps/web/src/routes/repositories/[repositoryId]/branches/[cloudBranchId]/patches/[changeId]/+layout.svelte index 655e2b2c2a..9964fd97b8 100644 --- a/apps/web/src/routes/repositories/[repositoryId]/branches/[cloudBranchId]/patches/[changeId]/+layout.svelte +++ b/apps/web/src/routes/repositories/[repositoryId]/branches/[cloudBranchId]/patches/[changeId]/+layout.svelte @@ -1,4 +1,5 @@ {@render children()} diff --git a/packages/shared/src/lib/cloud/patches/CloudPatchDetails.svelte b/packages/shared/src/lib/cloud/patches/CloudPatchDetails.svelte index 876e2599eb..fcbd39f800 100644 --- a/packages/shared/src/lib/cloud/patches/CloudPatchDetails.svelte +++ b/packages/shared/src/lib/cloud/patches/CloudPatchDetails.svelte @@ -1,9 +1,18 @@ {#if $optionalPatch.state === 'uninitialized'} @@ -38,7 +47,12 @@

Viewings: {patch.review.viewed.join(', ')}

Sign offs: {patch.review.signedOff.join(', ')}

-

Rejections: {patch.review.rejected.join(', ')}

+

Rejections: {patch.review.rejected.join(', ')}

+ +
+ + +
diff --git a/packages/shared/src/lib/cloud/patches/CloudPatchSections.svelte b/packages/shared/src/lib/cloud/patches/CloudPatchSections.svelte index 86ad623c45..c1400a49bc 100644 --- a/packages/shared/src/lib/cloud/patches/CloudPatchSections.svelte +++ b/packages/shared/src/lib/cloud/patches/CloudPatchSections.svelte @@ -1,12 +1,22 @@ +{#snippet sectionControls(identifier: string)} +
+ + +
+{/snippet} + {#if $optionalPatch.state === 'uninitialized'}

Loading...

{:else if $optionalPatch.state === 'not-found'} @@ -18,7 +28,7 @@ {#each patch.sections as section} {#if section.sectionType === 'diff'} - + {/if} {/each} {/if} @@ -27,10 +37,4 @@ .padding-bottom { margin-bottom: 16px; } - - .two-by-two { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 8px; - } diff --git a/packages/shared/src/lib/cloud/patches/DiffSection.svelte b/packages/shared/src/lib/cloud/patches/DiffSection.svelte index 11bc7190bb..b5e1780240 100644 --- a/packages/shared/src/lib/cloud/patches/DiffSection.svelte +++ b/packages/shared/src/lib/cloud/patches/DiffSection.svelte @@ -1,15 +1,20 @@
-

{diffSection.newPath || diffSection.oldPath}

+
+

{diffSection.newPath || diffSection.oldPath}

+ {@render sectionControls(diffSection.identifier)} +
{diffSection.diffPatch}
diff --git a/packages/shared/src/lib/cloud/patches/sections.ts b/packages/shared/src/lib/cloud/patches/sections.ts new file mode 100644 index 0000000000..8cd15cd751 --- /dev/null +++ b/packages/shared/src/lib/cloud/patches/sections.ts @@ -0,0 +1,69 @@ +import { get, type Readable } from 'svelte/store'; +import type { CloudPatchService } from '$lib/cloud/patches/service'; + +export class PatchSectionsService { + readonly canMoveSection: Readable; + + constructor(private readonly cloudPatchService: CloudPatchService) { + this.canMoveSection = cloudPatchService.canUpdatePatch; + } + + /** + * Moves the section to wards the page top + * + * This means that it will move the section towards the index 0 of the array + */ + moveSectionUp(identifier: string) { + if (!get(this.canMoveSection)) return; + + const patch = get(this.cloudPatchService.patch).value; + if (!patch) return; + + const identifiers = patch.sections.map((section) => section.identifier); + + const sectionIndex = identifiers.findIndex( + (listedIdentifier) => listedIdentifier === identifier + ); + + swap(identifiers, sectionIndex, sectionIndex - 1); + + this.cloudPatchService.update({ sectionOrder: identifiers }); + } + + /** + * Moves the section to wards the page bottom + * + * This means that it will move the section away from the index 0 of the array + */ + moveSectionDown(identifier: string) { + if (!get(this.canMoveSection)) return; + + const patch = get(this.cloudPatchService.patch).value; + if (!patch) return; + + const identifiers = patch.sections.map((section) => section.identifier); + + const sectionIndex = identifiers.findIndex( + (listedIdentifier) => listedIdentifier === identifier + ); + + swap(identifiers, sectionIndex, sectionIndex + 1); + + this.cloudPatchService.update({ sectionOrder: identifiers }); + } +} + +/** + * Swaps two elements in an array + * + * Returns false if the indicies were out of bounds + */ +function swap(input: unknown[], a: number, b: number): boolean { + const indiciesTooSmall = a < 0 || b < 0; + const indiciesTooLarge = a >= input.length || b >= input.length; + if (indiciesTooLarge || indiciesTooSmall) return false; + + [input[a], input[b]] = [input[b], input[a]]; + + return true; +} diff --git a/packages/shared/src/lib/cloud/patches/service.ts b/packages/shared/src/lib/cloud/patches/service.ts index 8aae40662f..b62378d253 100644 --- a/packages/shared/src/lib/cloud/patches/service.ts +++ b/packages/shared/src/lib/cloud/patches/service.ts @@ -8,11 +8,18 @@ import { writableDerived } from '$lib/storeUtils'; import { derived, get, type Readable, type Writable } from 'svelte/store'; import type { HttpClient } from '$lib/httpClient'; +interface PatchUpdate { + sign_off?: boolean; + section_order?: string[]; +} + export class ApiPatchService { readonly canGetPatch: Readable; + readonly canUpdatePatch: Readable; constructor(private readonly httpClient: HttpClient) { this.canGetPatch = httpClient.authenticationAvailable; + this.canUpdatePatch = httpClient.authenticationAvailable; } async getPatch(cloudBranchId: string, changeId: string): Promise { @@ -29,17 +36,47 @@ export class ApiPatchService { } } } + + async updatePatch( + cloudBranchId: string, + changeId: string, + params: PatchUpdate + ): Promise { + try { + return await this.httpClient.patch( + `patch_stack/${cloudBranchId}/patch/${changeId}`, + { + body: params + } + ); + } catch (e) { + // If the internet is down, silently fail + if (e instanceof TypeError) { + return undefined; + } else { + throw e; + } + } + } +} + +interface CloudPatchUpdate { + signOff?: boolean; + sectionOrder?: string[]; } export class CloudPatchService { readonly #apiPatch: Writable>; readonly patch: Readable>; + readonly canUpdatePatch: Readable; constructor( private readonly cloudBranchId: Readable, private readonly changeId: Readable, private readonly apiPatchService: ApiPatchService ) { + this.canUpdatePatch = apiPatchService.canUpdatePatch; + const values = derived( [cloudBranchId, changeId, apiPatchService.canGetPatch], (values) => values @@ -114,4 +151,24 @@ export class CloudPatchService { this.#apiPatch.set({ state: 'uninitialized' }); } } + + async update({ signOff, sectionOrder }: CloudPatchUpdate): Promise { + const cloudBranchId = get(this.cloudBranchId); + const changeId = get(this.changeId); + const canUpdatePatch = get(this.apiPatchService.canUpdatePatch); + + if (cloudBranchId && changeId && canUpdatePatch) { + const apiPatch = await this.apiPatchService.updatePatch(cloudBranchId, changeId, { + sign_off: signOff, + section_order: sectionOrder + }); + if (apiPatch) { + this.#apiPatch.set({ state: 'found', value: apiPatch }); + } else { + this.#apiPatch.set({ state: 'not-found' }); + } + } else { + this.#apiPatch.set({ state: 'uninitialized' }); + } + } } diff --git a/packages/shared/src/lib/cloud/types.ts b/packages/shared/src/lib/cloud/types.ts index 8eae37604a..271b4b8ca2 100644 --- a/packages/shared/src/lib/cloud/types.ts +++ b/packages/shared/src/lib/cloud/types.ts @@ -7,6 +7,7 @@ export type LoadableOptional = } | { state: 'uninitialized' | 'not-found'; + value?: undefined; }; export interface ApiPatchStatstics { @@ -69,7 +70,7 @@ export interface ApiPatch { export type ApiDiffSection = { id: number; section_type: 'diff'; - identifier?: string; + identifier: string; title?: string; position?: number; @@ -89,7 +90,7 @@ export type ApiDiffSection = { export type ApiTextSection = { id: number; section_type: 'text'; - identifier?: string; + identifier: string; title?: string; position?: number; @@ -135,7 +136,7 @@ export class CloudPatch { interface CloudSection { id: number; sectionType: string; - identifier?: string; + identifier: string; title?: string; position?: number; } @@ -143,7 +144,7 @@ interface CloudSection { export class CloudTextSection implements CloudSection { id: number; sectionType: 'text' = 'text' as const; - identifier?: string | undefined; + identifier: string; title?: string | undefined; position?: number | undefined; @@ -168,7 +169,7 @@ export class CloudTextSection implements CloudSection { export class CloudDiffSection implements CloudSection { id: number; sectionType: 'diff' = 'diff' as const; - identifier?: string | undefined; + identifier: string; title?: string | undefined; position?: number | undefined;