From f5428dcec7210c8cee9f1c6100e9512e217f4cfd Mon Sep 17 00:00:00 2001 From: Mattias Granlund Date: Thu, 1 Feb 2024 10:30:14 +0100 Subject: [PATCH] Make it possible to view remote commits using existing components - we need a union type rather than using File | RemoteFile everywhere --- .../src/lib/components/BaseBranch.svelte | 6 +- .../src/lib/components/BranchCard.svelte | 3 +- .../src/lib/components/BranchCommits.svelte | 8 +- .../src/lib/components/BranchFiles.svelte | 108 +++----------- .../lib/components/BranchFilesHeader.svelte | 88 +++++++++++ .../src/lib/components/BranchFilesList.svelte | 16 +- .../src/lib/components/BranchLane.svelte | 7 +- .../src/lib/components/CommitCard.svelte | 138 +++++++++++++----- .../src/lib/components/CommitList.svelte | 5 +- .../src/lib/components/CommitListItem.svelte | 13 +- .../src/lib/components/FileCard.svelte | 6 +- .../src/lib/components/FileCardHeader.svelte | 4 +- .../src/lib/components/FileListItem.svelte | 6 +- .../src/lib/components/FileStatusIcons.svelte | 4 +- .../src/lib/components/FileTree.svelte | 8 +- .../lib/components/RemoteBranchPreview.svelte | 7 +- .../src/lib/components/TreeListFile.svelte | 9 +- .../src/lib/components/UpstreamCommits.svelte | 77 ---------- gitbutler-ui/src/lib/dragging/draggables.ts | 10 +- gitbutler-ui/src/lib/utils/fileStatus.ts | 8 +- gitbutler-ui/src/lib/utils/metrics.ts | 4 +- gitbutler-ui/src/lib/utils/string.ts | 13 ++ gitbutler-ui/src/lib/vbranches/filetree.ts | 12 +- gitbutler-ui/src/lib/vbranches/ownership.ts | 4 +- gitbutler-ui/src/lib/vbranches/types.ts | 37 +++++ 25 files changed, 349 insertions(+), 252 deletions(-) create mode 100644 gitbutler-ui/src/lib/components/BranchFilesHeader.svelte delete mode 100644 gitbutler-ui/src/lib/components/UpstreamCommits.svelte create mode 100644 gitbutler-ui/src/lib/utils/string.ts diff --git a/gitbutler-ui/src/lib/components/BaseBranch.svelte b/gitbutler-ui/src/lib/components/BaseBranch.svelte index f151314ae..ce9ba2b9e 100644 --- a/gitbutler-ui/src/lib/components/BaseBranch.svelte +++ b/gitbutler-ui/src/lib/components/BaseBranch.svelte @@ -4,8 +4,9 @@ import Modal from '$lib/components/Modal.svelte'; import { projectMergeUpstreamWarningDismissed } from '$lib/config/config'; import { tooltip } from '$lib/utils/tooltip'; + import { writable } from 'svelte/store'; import type { BranchController } from '$lib/vbranches/branchController'; - import type { BaseBranch } from '$lib/vbranches/types'; + import type { BaseBranch, File, RemoteFile } from '$lib/vbranches/types'; export let base: BaseBranch; export let projectId: string; @@ -15,6 +16,7 @@ const mergeUpstreamWarningDismissed = projectMergeUpstreamWarningDismissed( branchController.projectId ); + const selectedFiles = writable<(File | RemoteFile)[]>([]); let updateTargetModal: Modal; let mergeUpstreamWarningDismissedCheckbox = false; @@ -52,6 +54,7 @@ diff --git a/gitbutler-ui/src/lib/components/BranchCommits.svelte b/gitbutler-ui/src/lib/components/BranchCommits.svelte index 5db7e7340..aba32cfc1 100644 --- a/gitbutler-ui/src/lib/components/BranchCommits.svelte +++ b/gitbutler-ui/src/lib/components/BranchCommits.svelte @@ -4,7 +4,8 @@ import type { BranchService } from '$lib/branches/service'; import type { GitHubService } from '$lib/github/service'; import type { BranchController } from '$lib/vbranches/branchController'; - import type { BaseBranch, Branch } from '$lib/vbranches/types'; + import type { BaseBranch, Branch, File, RemoteFile } from '$lib/vbranches/types'; + import type { Writable } from 'svelte/store'; export let project: Project; export let branch: Branch; @@ -12,6 +13,7 @@ export let githubService: GitHubService; export let branchController: BranchController; export let branchService: BranchService; + export let selectedFiles: Writable<(File | RemoteFile)[]>; export let isUnapplied: boolean; export let branchCount: number; @@ -26,6 +28,7 @@ {branchCount} {githubService} {isUnapplied} + {selectedFiles} type="upstream" /> {/if} diff --git a/gitbutler-ui/src/lib/components/BranchFiles.svelte b/gitbutler-ui/src/lib/components/BranchFiles.svelte index e6dbde7f3..f662b05be 100644 --- a/gitbutler-ui/src/lib/components/BranchFiles.svelte +++ b/gitbutler-ui/src/lib/components/BranchFiles.svelte @@ -1,10 +1,7 @@ {#if branch.active && branch.conflicted} @@ -64,38 +28,21 @@ {/if}
-
-
- {#if showCheckboxes && selectedListMode == 'list' && branch.files.length > 1} - { - if (e.detail) { - selectAll(selectedOwnership, branch.files); - } else { - selectedOwnership.update((ownership) => ownership.clear()); - } - }} - /> - {/if} -
- Changes - -
-
- - - - +
+
{#if branch.files.length > 0} -
- +
{#if selectedListMode == 'list'} {:else} diff --git a/gitbutler-ui/src/lib/components/BranchFilesHeader.svelte b/gitbutler-ui/src/lib/components/BranchFilesHeader.svelte new file mode 100644 index 000000000..70052f7c2 --- /dev/null +++ b/gitbutler-ui/src/lib/components/BranchFilesHeader.svelte @@ -0,0 +1,88 @@ + + +
+
+ {#if showCheckboxes && selectedListMode == 'list' && files.length > 1} + { + if (e.detail) { + selectAll(selectedOwnership, files); + } else { + selectedOwnership.update((ownership) => ownership.clear()); + } + }} + /> + {/if} +
+ Changes + +
+
+ + + + +
+ + diff --git a/gitbutler-ui/src/lib/components/BranchFilesList.svelte b/gitbutler-ui/src/lib/components/BranchFilesList.svelte index ec87cda2f..4c16e99fe 100644 --- a/gitbutler-ui/src/lib/components/BranchFilesList.svelte +++ b/gitbutler-ui/src/lib/components/BranchFilesList.svelte @@ -2,21 +2,25 @@ import FileListItem from './FileListItem.svelte'; import { sortLikeFileTree } from '$lib/vbranches/filetree'; import type { Ownership } from '$lib/vbranches/ownership'; - import type { Branch, File } from '$lib/vbranches/types'; + import type { File, RemoteFile } from '$lib/vbranches/types'; import type { Writable } from 'svelte/store'; - export let branch: Branch; + export let branchId: string; + export let files: (File | RemoteFile)[]; export let selectedOwnership: Writable; export let isUnapplied = false; export let showCheckboxes = false; - export let selectedFiles: Writable; + export let selectedFiles: Writable<(File | RemoteFile)[]>; + export let allowMultiple = false; + + $: console.log(selectedFiles); -{#each sortLikeFileTree(branch.files) as file (file.id)} +{#each sortLikeFileTree(files) as file (file.id)} fileIds.filter((f) => f.id != file.id)); } else if (isAlreadySelected) { $selectedFiles = []; - } else if (e.shiftKey) { + } else if (e.shiftKey && allowMultiple) { selectedFiles.update((files) => [file, ...files]); } else { $selectedFiles = [file]; diff --git a/gitbutler-ui/src/lib/components/BranchLane.svelte b/gitbutler-ui/src/lib/components/BranchLane.svelte index 725339fe8..c77aaec56 100644 --- a/gitbutler-ui/src/lib/components/BranchLane.svelte +++ b/gitbutler-ui/src/lib/components/BranchLane.svelte @@ -2,13 +2,13 @@ import BranchCard from './BranchCard.svelte'; import FileCard from './FileCard.svelte'; import { Ownership } from '$lib/vbranches/ownership'; + import { RemoteFile, type BaseBranch, type Branch, type File } from '$lib/vbranches/types'; import { writable, type Writable } from 'svelte/store'; import type { User, getCloudApiClient } from '$lib/backend/cloud'; import type { Project } from '$lib/backend/projects'; import type { BranchService } from '$lib/branches/service'; import type { GitHubService } from '$lib/github/service'; import type { BranchController } from '$lib/vbranches/branchController'; - import type { BaseBranch, Branch, File } from '$lib/vbranches/types'; export let branch: Branch; export let isUnapplied = false; @@ -29,8 +29,11 @@ let commitBoxOpen: Writable; - function setSelected(files: File[], branch: Branch) { + function setSelected(files: (File | RemoteFile)[], branch: Branch) { if (files.length == 0) return undefined; + if (files.length == 1 && files[0] instanceof RemoteFile) return files[0]; + + // If regular file selected but not found in branch files then clear selection. const match = branch.files?.find((f) => files[0].id == f.id); if (!match) $selectedFiles = []; return match; diff --git a/gitbutler-ui/src/lib/components/CommitCard.svelte b/gitbutler-ui/src/lib/components/CommitCard.svelte index f6e7daaff..aab1ff799 100644 --- a/gitbutler-ui/src/lib/components/CommitCard.svelte +++ b/gitbutler-ui/src/lib/components/CommitCard.svelte @@ -1,5 +1,8 @@
-
- - {commit.description} - - {#if isHeadCommit && !isUnapplied} - { - e.stopPropagation(); - resetHeadCommit(); - }}>Undo - {/if} -
- -
-
- Gravatar for {commit.author.email} - {commit.author.name} +
+
+ + {commit.description} + + {#if isHeadCommit && !isUnapplied} + { + e.stopPropagation(); + resetHeadCommit(); + }}>Undo + {/if} +
+ +
+
+ Gravatar for {commit.author.email} + {commit.author.name} +
+ + +
- - -
+ {#if showFiles} +
+ +
+
+ {#if selectedListMode == 'list'} + + {:else} + + {/if} +
+ {/if}
@@ -168,7 +210,6 @@ flex-direction: column; cursor: default; gap: var(--space-10); - padding: var(--space-12); border-radius: var(--space-6); background-color: var(--clr-theme-container-light); border: 1px solid var(--clr-theme-container-outline-light); @@ -183,6 +224,7 @@ display: flex; align-items: center; gap: var(--space-8); + padding: var(--space-12) var(--space-12) 0 var(--space-12); } .commit__description { @@ -197,6 +239,22 @@ display: flex; align-items: center; gap: var(--space-8); + padding: 0 var(--space-12) var(--space-12) var(--space-12); + } + + .commit__files { + padding-top: 0; + padding-left: var(--space-12); + padding-right: var(--space-12); + padding-bottom: var(--space-12); + } + + .commit__files-header { + border-top: 1px solid var(--clr-theme-container-outline-light); + padding-top: var(--space-12); + padding-bottom: var(--space-12); + padding-left: var(--space-20); + padding-right: var(--space-12); } .commit__author { diff --git a/gitbutler-ui/src/lib/components/CommitList.svelte b/gitbutler-ui/src/lib/components/CommitList.svelte index 1ae0e1ba5..8a5474baa 100644 --- a/gitbutler-ui/src/lib/components/CommitList.svelte +++ b/gitbutler-ui/src/lib/components/CommitList.svelte @@ -6,7 +6,8 @@ import type { BranchService } from '$lib/branches/service'; import type { GitHubService } from '$lib/github/service'; import type { BranchController } from '$lib/vbranches/branchController'; - import type { BaseBranch, Branch, CommitStatus } from '$lib/vbranches/types'; + import type { BaseBranch, Branch, CommitStatus, File, RemoteFile } from '$lib/vbranches/types'; + import type { Writable } from 'svelte/store'; export let branch: Branch; export let base: BaseBranch | undefined | null; @@ -15,6 +16,7 @@ export let type: CommitStatus; export let githubService: GitHubService; export let branchService: BranchService; + export let selectedFiles: Writable<(File | RemoteFile)[]>; export let isUnapplied: boolean; export let branchCount: number = 0; @@ -47,6 +49,7 @@ {base} {project} {isUnapplied} + {selectedFiles} isChained={idx != commits.length - 1} isHeadCommit={commit.id === headCommit?.id} /> diff --git a/gitbutler-ui/src/lib/components/CommitListItem.svelte b/gitbutler-ui/src/lib/components/CommitListItem.svelte index e5112a478..adede0c43 100644 --- a/gitbutler-ui/src/lib/components/CommitListItem.svelte +++ b/gitbutler-ui/src/lib/components/CommitListItem.svelte @@ -11,8 +11,15 @@ } from '$lib/dragging/draggables'; import { dropzone } from '$lib/dragging/dropzone'; import { filesToOwnership } from '$lib/vbranches/ownership'; - import { RemoteCommit, type BaseBranch, type Branch, type Commit } from '$lib/vbranches/types'; - import { get } from 'svelte/store'; + import { + RemoteCommit, + type BaseBranch, + type Branch, + type Commit, + type File, + RemoteFile + } from '$lib/vbranches/types'; + import { get, type Writable } from 'svelte/store'; import type { Project } from '$lib/backend/projects'; import type { BranchController } from '$lib/vbranches/branchController'; @@ -24,6 +31,7 @@ export let isChained: boolean; export let isUnapplied = false; export let branchController: BranchController; + export let selectedFiles: Writable<(File | RemoteFile)[]>; function acceptAmend(commit: Commit | RemoteCommit) { if (commit instanceof RemoteCommit) { @@ -139,6 +147,7 @@ {resetHeadCommit} {isUnapplied} {branchController} + {selectedFiles} projectPath={project.path} />
diff --git a/gitbutler-ui/src/lib/components/FileCard.svelte b/gitbutler-ui/src/lib/components/FileCard.svelte index 3cea4cd97..e2eb2350c 100644 --- a/gitbutler-ui/src/lib/components/FileCard.svelte +++ b/gitbutler-ui/src/lib/components/FileCard.svelte @@ -12,12 +12,12 @@ import { slide } from 'svelte/transition'; import type { BranchController } from '$lib/vbranches/branchController'; import type { Ownership } from '$lib/vbranches/ownership'; - import type { File } from '$lib/vbranches/types'; + import type { File, RemoteFile } from '$lib/vbranches/types'; import type { Writable } from 'svelte/store'; export let projectId: string; export let branchId: string; - export let file: File; + export let file: File | RemoteFile; export let conflicted: boolean; export let projectPath: string | undefined; export let branchController: BranchController; @@ -35,7 +35,7 @@ let sections: (HunkSection | ContentSection)[] = []; - function parseFile(file: File) { + function parseFile(file: File | RemoteFile) { // When we toggle expansion status on sections we need to assign // `sections = sections` to redraw, and why we do not use a reactive // variable. diff --git a/gitbutler-ui/src/lib/components/FileCardHeader.svelte b/gitbutler-ui/src/lib/components/FileCardHeader.svelte index 4af760307..da23ee504 100644 --- a/gitbutler-ui/src/lib/components/FileCardHeader.svelte +++ b/gitbutler-ui/src/lib/components/FileCardHeader.svelte @@ -6,9 +6,9 @@ import { computeFileStatus } from '$lib/utils/fileStatus'; import { computeAddedRemovedByFiles } from '$lib/utils/metrics'; import { createEventDispatcher } from 'svelte'; - import type { File } from '$lib/vbranches/types'; + import type { File, RemoteFile } from '$lib/vbranches/types'; - export let file: File; + export let file: File | RemoteFile; export let isFileLocked: boolean; const dispatch = createEventDispatcher<{ close: void }>(); diff --git a/gitbutler-ui/src/lib/components/FileListItem.svelte b/gitbutler-ui/src/lib/components/FileListItem.svelte index 86f951f21..3fedca763 100644 --- a/gitbutler-ui/src/lib/components/FileListItem.svelte +++ b/gitbutler-ui/src/lib/components/FileListItem.svelte @@ -5,16 +5,16 @@ import { draggableFile } from '$lib/dragging/draggables'; import { getVSIFileIcon } from '$lib/ext-icons'; import type { Ownership } from '$lib/vbranches/ownership'; - import type { File } from '$lib/vbranches/types'; + import type { File, RemoteFile } from '$lib/vbranches/types'; import type { Writable } from 'svelte/store'; export let branchId: string; - export let file: File; + export let file: File | RemoteFile; export let isUnapplied: boolean; export let selected: boolean; export let showCheckbox: boolean = false; export let selectedOwnership: Writable; - export let selectedFiles: Writable; + export let selectedFiles: Writable<(File | RemoteFile)[]>; let checked = false; let indeterminate = false; diff --git a/gitbutler-ui/src/lib/components/FileStatusIcons.svelte b/gitbutler-ui/src/lib/components/FileStatusIcons.svelte index 43e8883ad..701f5809c 100644 --- a/gitbutler-ui/src/lib/components/FileStatusIcons.svelte +++ b/gitbutler-ui/src/lib/components/FileStatusIcons.svelte @@ -2,9 +2,9 @@ import FileStatusCircle from './FileStatusCircle.svelte'; import Icon from '$lib/components/Icon.svelte'; import { computeFileStatus } from '$lib/utils/fileStatus'; - import type { File } from '$lib/vbranches/types'; + import type { File, RemoteFile } from '$lib/vbranches/types'; - export let file: File; + export let file: File | RemoteFile; $: isLocked = file.hunks.some((h) => h.locked); diff --git a/gitbutler-ui/src/lib/components/FileTree.svelte b/gitbutler-ui/src/lib/components/FileTree.svelte index 5978a4046..570fa3488 100644 --- a/gitbutler-ui/src/lib/components/FileTree.svelte +++ b/gitbutler-ui/src/lib/components/FileTree.svelte @@ -7,7 +7,7 @@ import TreeListFolder from './TreeListFolder.svelte'; import type { TreeNode } from '$lib/vbranches/filetree'; import type { Ownership } from '$lib/vbranches/ownership'; - import type { File } from '$lib/vbranches/types'; + import type { File, RemoteFile } from '$lib/vbranches/types'; import type { Writable } from 'svelte/store'; export let expanded = true; @@ -15,9 +15,10 @@ export let isRoot = false; export let showCheckboxes = false; export let selectedOwnership: Writable; - export let selectedFiles: Writable; + export let selectedFiles: Writable<(File | RemoteFile)[]>; export let branchId: string; export let isUnapplied: boolean; + export let allowMultiple = false; function isNodeChecked(selectedOwnership: Ownership, node: TreeNode): boolean { if (node.file) { @@ -91,12 +92,13 @@ {selectedFiles} showCheckbox={showCheckboxes} on:click={(e) => { + e.stopPropagation(); const isAlreadySelected = $selectedFiles.includes(file); if (isAlreadySelected && e.shiftKey) { selectedFiles.update((fileIds) => fileIds.filter((f) => f.id != file.id)); } else if (isAlreadySelected) { $selectedFiles = []; - } else if (e.shiftKey) { + } else if (e.shiftKey && allowMultiple) { selectedFiles.update((files) => [file, ...files]); } else { $selectedFiles = [file]; diff --git a/gitbutler-ui/src/lib/components/RemoteBranchPreview.svelte b/gitbutler-ui/src/lib/components/RemoteBranchPreview.svelte index bbdf499c7..71e41fec0 100644 --- a/gitbutler-ui/src/lib/components/RemoteBranchPreview.svelte +++ b/gitbutler-ui/src/lib/components/RemoteBranchPreview.svelte @@ -2,12 +2,15 @@ import Button from '$lib/components/Button.svelte'; import CommitCard from '$lib/components/CommitCard.svelte'; import type { BranchController } from '$lib/vbranches/branchController'; - import type { RemoteBranch } from '$lib/vbranches/types'; + import type { File, RemoteBranch, RemoteFile } from '$lib/vbranches/types'; + import { writable } from 'svelte/store'; export let branch: RemoteBranch | undefined; export let projectId: string; export let projectPath: string; export let branchController: BranchController; + + const selectedFiles = writable<(File | RemoteFile)[]>([]); {#if branch != undefined} @@ -24,7 +27,7 @@ {#if branch.commits && branch.commits.length > 0}
{#each branch.commits as commit} - + {/each}
{/if} diff --git a/gitbutler-ui/src/lib/components/TreeListFile.svelte b/gitbutler-ui/src/lib/components/TreeListFile.svelte index eda8415f3..1c8bb7c9f 100644 --- a/gitbutler-ui/src/lib/components/TreeListFile.svelte +++ b/gitbutler-ui/src/lib/components/TreeListFile.svelte @@ -5,16 +5,16 @@ import { draggableFile } from '$lib/dragging/draggables'; import { getVSIFileIcon } from '$lib/ext-icons'; import type { Ownership } from '$lib/vbranches/ownership'; - import type { File } from '$lib/vbranches/types'; + import type { File, RemoteFile } from '$lib/vbranches/types'; import type { Writable } from 'svelte/store'; export let branchId: string; - export let file: File; + export let file: File | RemoteFile; export let selected: boolean; export let isUnapplied: boolean; export let showCheckbox: boolean = false; export let selectedOwnership: Writable; - export let selectedFiles: Writable; + export let selectedFiles: Writable<(File | RemoteFile)[]>; let checked = false; let indeterminate = false; @@ -103,16 +103,19 @@ display: flex; align-items: center; gap: var(--space-10); + overflow: hidden; } .name-wrapper { display: flex; align-items: center; gap: var(--space-6); + overflow: hidden; } .name { color: var(--clr-theme-scale-ntrl-0); text-overflow: ellipsis; overflow: hidden; + white-space: nowrap; } .selected { background-color: var(--clr-theme-scale-pop-80); diff --git a/gitbutler-ui/src/lib/components/UpstreamCommits.svelte b/gitbutler-ui/src/lib/components/UpstreamCommits.svelte deleted file mode 100644 index e546a2636..000000000 --- a/gitbutler-ui/src/lib/components/UpstreamCommits.svelte +++ /dev/null @@ -1,77 +0,0 @@ - - -{#if upstream} -
-
-
- {upstream.commits.length} - upstream {upstream.commits.length > 1 ? 'commits' : 'commit'} -
- -
-
- {#if upstreamCommitsShown} -
- {#each upstream.commits as commit (commit.id)} -
- -
- {/each} -
- {#if branchCount > 1} -
- You have {branchCount} active branches. To merge upstream work, we will unapply all other - branches. -
- {/if} - -
-
- {/if} -{/if} diff --git a/gitbutler-ui/src/lib/dragging/draggables.ts b/gitbutler-ui/src/lib/dragging/draggables.ts index 389d895b3..b938eff04 100644 --- a/gitbutler-ui/src/lib/dragging/draggables.ts +++ b/gitbutler-ui/src/lib/dragging/draggables.ts @@ -1,4 +1,4 @@ -import type { Commit, File, Hunk, RemoteCommit } from '../vbranches/types'; +import type { Commit, File, Hunk, RemoteCommit, RemoteFile } from '../vbranches/types'; import type { Writable } from 'svelte/store'; export function nonDraggable() { @@ -23,11 +23,15 @@ export function isDraggableHunk(obj: any): obj is DraggableHunk { export type DraggableFile = { branchId: string; - files: Writable; + files: Writable<(File | RemoteFile)[]>; current: File; }; -export function draggableFile(branchId: string, current: File, files: Writable) { +export function draggableFile( + branchId: string, + current: File | RemoteFile, + files: Writable<(File | RemoteFile)[]> +) { return { data: { branchId, current, files } }; } diff --git a/gitbutler-ui/src/lib/utils/fileStatus.ts b/gitbutler-ui/src/lib/utils/fileStatus.ts index 48e954a7f..2769669a4 100644 --- a/gitbutler-ui/src/lib/utils/fileStatus.ts +++ b/gitbutler-ui/src/lib/utils/fileStatus.ts @@ -1,8 +1,12 @@ -import type { File } from '$lib/vbranches/types'; +import { RemoteFile, type File } from '$lib/vbranches/types'; export type FileStatus = 'A' | 'M' | 'D'; -export function computeFileStatus(file: File): FileStatus { +export function computeFileStatus(file: File | RemoteFile): FileStatus { + if (file instanceof RemoteFile) { + // TODO: How do we compute this for remote files? + return 'M'; + } if (file.hunks.length == 1) { const changeType = file.hunks[0].changeType; if (changeType == 'added') { diff --git a/gitbutler-ui/src/lib/utils/metrics.ts b/gitbutler-ui/src/lib/utils/metrics.ts index f945e1e11..b8c88f2a8 100644 --- a/gitbutler-ui/src/lib/utils/metrics.ts +++ b/gitbutler-ui/src/lib/utils/metrics.ts @@ -1,7 +1,7 @@ import { HunkSection, type ContentSection } from './fileSections'; -import type { File } from '$lib/vbranches/types'; +import type { File, RemoteFile } from '$lib/vbranches/types'; -export function computeAddedRemovedByFiles(...files: File[]) { +export function computeAddedRemovedByFiles(...files: (File | RemoteFile)[]) { return files .flatMap((f) => f.hunks) .map((h) => h.diff.split('\n')) diff --git a/gitbutler-ui/src/lib/utils/string.ts b/gitbutler-ui/src/lib/utils/string.ts new file mode 100644 index 000000000..6b2680861 --- /dev/null +++ b/gitbutler-ui/src/lib/utils/string.ts @@ -0,0 +1,13 @@ +export function hashCode(s: string) { + let hash = 0; + let chr; + let i; + + if (s.length === 0) return hash.toString(); + for (i = 0; i < s.length; i++) { + chr = s.charCodeAt(i); + hash = (hash << 5) - hash + chr; + hash |= 0; // Convert to 32bit integer + } + return hash.toString(); +} diff --git a/gitbutler-ui/src/lib/vbranches/filetree.ts b/gitbutler-ui/src/lib/vbranches/filetree.ts index 998daa41a..4990a2978 100644 --- a/gitbutler-ui/src/lib/vbranches/filetree.ts +++ b/gitbutler-ui/src/lib/vbranches/filetree.ts @@ -4,11 +4,11 @@ * This module provides support for tranforming a list of files into a * hirerarchical structure for easy rendering. */ -import type { File } from './types'; +import type { File, RemoteFile } from './types'; export interface TreeNode { name: string; - file?: File; + file?: File | RemoteFile; children: TreeNode[]; parent?: TreeNode; } @@ -40,7 +40,7 @@ export function sortChildren(node: TreeNode) { } } -export function filesToFileTree(files: File[]): TreeNode { +export function filesToFileTree(files: (File | RemoteFile)[]): TreeNode { const acc: TreeNode = { name: 'root', children: [] }; files.forEach((f) => { const pathParts = f.path.split('/'); @@ -51,8 +51,8 @@ export function filesToFileTree(files: File[]): TreeNode { return acc; } -function fileTreeToList(node: TreeNode): File[] { - const list: File[] = []; +function fileTreeToList(node: TreeNode): (File | RemoteFile)[] { + const list: (File | RemoteFile)[] = []; if (node.file) list.push(node.file); node.children.forEach((child) => { list.push(...fileTreeToList(child)); @@ -61,6 +61,6 @@ function fileTreeToList(node: TreeNode): File[] { } // Sorts a file list the same way it is sorted in a file tree -export function sortLikeFileTree(files: File[]): File[] { +export function sortLikeFileTree(files: (File | RemoteFile)[]): (File | RemoteFile)[] { return fileTreeToList(filesToFileTree(files)); } diff --git a/gitbutler-ui/src/lib/vbranches/ownership.ts b/gitbutler-ui/src/lib/vbranches/ownership.ts index dd68818b7..dac003d19 100644 --- a/gitbutler-ui/src/lib/vbranches/ownership.ts +++ b/gitbutler-ui/src/lib/vbranches/ownership.ts @@ -1,6 +1,6 @@ -import type { Branch, File } from './types'; +import type { Branch, File, RemoteFile } from './types'; -export function filesToOwnership(files: File[]) { +export function filesToOwnership(files: (File | RemoteFile)[]) { return files.map((f) => `${f.path}:${f.hunks.map(({ id }) => id).join(',')}`).join('\n'); } diff --git a/gitbutler-ui/src/lib/vbranches/types.ts b/gitbutler-ui/src/lib/vbranches/types.ts index 471a23cc9..c5a39ba17 100644 --- a/gitbutler-ui/src/lib/vbranches/types.ts +++ b/gitbutler-ui/src/lib/vbranches/types.ts @@ -1,5 +1,6 @@ import 'reflect-metadata'; import { Type, Transform } from 'class-transformer'; +import { hashCode } from '$lib/utils/string'; export type ChangeType = /// Entry does not exist in old version @@ -130,6 +131,14 @@ export class RemoteCommit { export class RemoteHunk { diff!: string; + + get id(): string { + return hashCode(this.diff); + } + + get locked() { + return false; + } } export class RemoteFile { @@ -137,6 +146,34 @@ export class RemoteFile { @Type(() => RemoteHunk) hunks!: RemoteHunk[]; binary!: boolean; + + get id(): string { + return this.path; + } + + get filename(): string { + return this.path.replace(/^.*[\\/]/, ''); + } + + get justpath() { + return this.path.split('/').slice(0, -1).join('/'); + } + + get locked() { + return false; + } + + get large() { + return false; + } + + get conflicted() { + return false; + } + + get hunkIds() { + return this.hunks.map((h) => h.id); + } } export interface Author {