mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-11-30 01:17:37 +03:00
Use same FileDiff in FileCard and CommitCard
This commit is contained in:
parent
d838d39600
commit
2138a5cc03
@ -10,6 +10,8 @@
|
||||
export let base: BaseBranch;
|
||||
export let projectId: string;
|
||||
export let branchController: BranchController;
|
||||
export let projectPath: string;
|
||||
|
||||
const mergeUpstreamWarningDismissed = projectMergeUpstreamWarningDismissed(
|
||||
branchController.projectId
|
||||
);
|
||||
@ -47,7 +49,13 @@
|
||||
<div class="flex h-full">
|
||||
<div class="z-20 flex w-full flex-col gap-2">
|
||||
{#each base.upstreamCommits as commit}
|
||||
<CommitCard {commit} {projectId} commitUrl={base.commitUrl(commit.id)} />
|
||||
<CommitCard
|
||||
{commit}
|
||||
{projectId}
|
||||
commitUrl={base.commitUrl(commit.id)}
|
||||
{projectPath}
|
||||
{branchController}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
@ -65,7 +73,13 @@
|
||||
</div>
|
||||
<div class="flex flex-col gap-y-2">
|
||||
{#each base.recentCommits as commit}
|
||||
<CommitCard {commit} {projectId} commitUrl={base.commitUrl(commit.id)} />
|
||||
<CommitCard
|
||||
{commit}
|
||||
{projectId}
|
||||
commitUrl={base.commitUrl(commit.id)}
|
||||
{projectPath}
|
||||
{branchController}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,6 +1,5 @@
|
||||
<script lang="ts">
|
||||
import HunkLine from './HunkLine.svelte';
|
||||
import { invoke } from '$lib/backend/ipc';
|
||||
import FileDiff from './FileDiff.svelte';
|
||||
import Button from '$lib/components/Button.svelte';
|
||||
import Modal from '$lib/components/Modal.svelte';
|
||||
import Tag from '$lib/components/Tag.svelte';
|
||||
@ -8,10 +7,11 @@
|
||||
import { draggable } from '$lib/dragging/draggable';
|
||||
import { draggableCommit, nonDraggable } from '$lib/dragging/draggables';
|
||||
import { getVSIFileIcon } from '$lib/ext-icons';
|
||||
import { ContentSection, HunkSection, parseFileSections } from '$lib/utils/fileSections';
|
||||
import { RemoteFile, type RemoteCommit, Commit } from '$lib/vbranches/types';
|
||||
import { listRemoteCommitFiles } from '$lib/vbranches/remoteCommits';
|
||||
import { RemoteCommit, Commit, RemoteFile } from '$lib/vbranches/types';
|
||||
import { open } from '@tauri-apps/api/shell';
|
||||
import { plainToInstance } from 'class-transformer';
|
||||
import type { ContentSection, HunkSection } from '$lib/utils/fileSections';
|
||||
import type { BranchController } from '$lib/vbranches/branchController';
|
||||
|
||||
export let commit: Commit | RemoteCommit;
|
||||
export let projectId: string;
|
||||
@ -19,23 +19,17 @@
|
||||
export let isHeadCommit: boolean = false;
|
||||
export let resetHeadCommit: () => void | undefined = () => undefined;
|
||||
export let isUnapplied = false;
|
||||
export let branchController: BranchController;
|
||||
export let projectPath: string;
|
||||
|
||||
let previewCommitModal: Modal;
|
||||
let minWidth = 2;
|
||||
|
||||
let entries: [string, (ContentSection | HunkSection)[]][] = [];
|
||||
let entries: [RemoteFile, (ContentSection | HunkSection)[]][] = [];
|
||||
let isLoading = false;
|
||||
|
||||
async function loadEntries() {
|
||||
isLoading = true;
|
||||
entries = plainToInstance(
|
||||
RemoteFile,
|
||||
await invoke<any[]>('list_remote_commit_files', { projectId, commitOid: commit.id })
|
||||
)
|
||||
.map(
|
||||
(file) => [file.path, parseFileSections(file)] as [string, (ContentSection | HunkSection)[]]
|
||||
)
|
||||
.sort((a, b) => a[0].localeCompare(b[0]));
|
||||
entries = await listRemoteCommitFiles(projectId, commit.id);
|
||||
isLoading = false;
|
||||
}
|
||||
|
||||
@ -119,38 +113,35 @@
|
||||
<div class="border-gray-900 h-8 w-8 animate-spin rounded-full border-b-2" />
|
||||
</div>
|
||||
{:else}
|
||||
{#each entries as [filepath, sections]}
|
||||
{#each entries as [remoteFile, sections]}
|
||||
<div class="commit-modal__file-section">
|
||||
<div
|
||||
class="text-color-3 flex flex-grow items-center overflow-hidden text-ellipsis whitespace-nowrap font-bold"
|
||||
title={filepath}
|
||||
title={remoteFile.path}
|
||||
>
|
||||
<img
|
||||
src={getVSIFileIcon(filepath)}
|
||||
src={getVSIFileIcon(remoteFile.path)}
|
||||
alt="js"
|
||||
width="13"
|
||||
style="width: 0.8125rem"
|
||||
class="mr-1 inline"
|
||||
/>
|
||||
|
||||
{filepath}
|
||||
{remoteFile.path}
|
||||
</div>
|
||||
<div class="commit-modal__code-container custom-scrollbar">
|
||||
<div class="commit-modal__code-wrapper">
|
||||
{#each sections as section}
|
||||
{#if 'hunk' in section}
|
||||
{#each section.subSections as subsection}
|
||||
{#each subsection.lines.slice(0, subsection.expanded ? subsection.lines.length : 0) as line}
|
||||
<HunkLine
|
||||
{line}
|
||||
{minWidth}
|
||||
sectionType={subsection.sectionType}
|
||||
filePath={filepath}
|
||||
/>
|
||||
{/each}
|
||||
{/each}
|
||||
{/if}
|
||||
{/each}
|
||||
<FileDiff
|
||||
filePath={remoteFile.path}
|
||||
isBinary={remoteFile.binary}
|
||||
isLarge={false}
|
||||
{projectPath}
|
||||
{isUnapplied}
|
||||
{branchController}
|
||||
selectable={false}
|
||||
branchId={commit instanceof Commit ? commit.branchId : undefined}
|
||||
{sections}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -138,6 +138,8 @@
|
||||
{isHeadCommit}
|
||||
{resetHeadCommit}
|
||||
{isUnapplied}
|
||||
{branchController}
|
||||
projectPath={project.path}
|
||||
/>
|
||||
</div>
|
||||
<!-- <div class="reset-head">
|
||||
|
@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import FileCardHeader from './FileCardHeader.svelte';
|
||||
import FileDiff from './FileDiff.svelte';
|
||||
import Resizer from '$lib/components/Resizer.svelte';
|
||||
import ScrollableContainer from '$lib/components/ScrollableContainer.svelte';
|
||||
import { persisted } from '$lib/persisted/persisted';
|
||||
@ -13,7 +14,6 @@
|
||||
import type { Ownership } from '$lib/vbranches/ownership';
|
||||
import type { File } from '$lib/vbranches/types';
|
||||
import type { Writable } from 'svelte/store';
|
||||
import FileDiff from './FileDiff.svelte';
|
||||
|
||||
export let projectId: string;
|
||||
export let branchId: string;
|
||||
@ -43,17 +43,6 @@
|
||||
}
|
||||
$: parseFile(file);
|
||||
|
||||
$: maxLineNumber = sections[sections.length - 1]?.maxLineNumber;
|
||||
|
||||
function getGutterMinWidth(max: number) {
|
||||
if (max >= 1000) return 2;
|
||||
if (max >= 100) return 1.5;
|
||||
if (max >= 10) return 1.25;
|
||||
return 1;
|
||||
}
|
||||
|
||||
$: minWidth = getGutterMinWidth(maxLineNumber);
|
||||
|
||||
$: isFileLocked = sections
|
||||
.filter((section): section is HunkSection => section instanceof HunkSection)
|
||||
.some((section) => section.hunk.locked);
|
||||
@ -82,7 +71,10 @@
|
||||
|
||||
<ScrollableContainer wide>
|
||||
<FileDiff
|
||||
{file}
|
||||
filePath={file.path}
|
||||
isLarge={file.large}
|
||||
isBinary={file.binary}
|
||||
{sections}
|
||||
{projectPath}
|
||||
{isFileLocked}
|
||||
{isUnapplied}
|
||||
@ -148,31 +140,6 @@
|
||||
max-height: 100%;
|
||||
flex-grow: 1;
|
||||
}
|
||||
.hunks {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
max-height: 100%;
|
||||
flex-shrink: 0;
|
||||
padding: var(--space-16);
|
||||
gap: var(--space-16);
|
||||
}
|
||||
.hunk-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-10);
|
||||
}
|
||||
.indicators {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
}
|
||||
.added {
|
||||
color: #45b156;
|
||||
}
|
||||
.removed {
|
||||
color: #ff3e00;
|
||||
}
|
||||
|
||||
@keyframes wiggle {
|
||||
0% {
|
||||
|
@ -1,28 +1,40 @@
|
||||
<script lang="ts">
|
||||
import HunkViewer from './HunkViewer.svelte';
|
||||
import Icon from './Icon.svelte';
|
||||
import { computeAddedRemovedByFiles, computeAddedRemovedByHunk } from '$lib/utils/metrics';
|
||||
import { computeAddedRemovedByHunk } from '$lib/utils/metrics';
|
||||
import type { HunkSection, ContentSection } from '$lib/utils/fileSections';
|
||||
import type { BranchController } from '$lib/vbranches/branchController';
|
||||
import type { Ownership } from '$lib/vbranches/ownership';
|
||||
import type { File } from '$lib/vbranches/types';
|
||||
import type { Writable } from 'svelte/store';
|
||||
|
||||
export let branchId: string;
|
||||
export let file: File;
|
||||
export let sections: (HunkSection | ContentSection)[] = [];
|
||||
export let branchId: string | undefined;
|
||||
export let filePath: string;
|
||||
export let isBinary: boolean;
|
||||
export let isLarge: boolean;
|
||||
export let sections: (HunkSection | ContentSection)[];
|
||||
export let projectPath: string | undefined;
|
||||
export let branchController: BranchController;
|
||||
export let isUnapplied: boolean;
|
||||
export let selectable = false;
|
||||
export let selectedOwnership: Writable<Ownership>;
|
||||
export let isFileLocked: boolean;
|
||||
export let selectedOwnership: Writable<Ownership> | undefined = undefined;
|
||||
export let isFileLocked = false;
|
||||
|
||||
function getGutterMinWidth(max: number) {
|
||||
if (max >= 10000) return 2.5;
|
||||
if (max >= 1000) return 2;
|
||||
if (max >= 100) return 1.5;
|
||||
if (max >= 10) return 1.25;
|
||||
return 1;
|
||||
}
|
||||
|
||||
$: maxLineNumber = sections[sections.length - 1]?.maxLineNumber;
|
||||
$: minWidth = getGutterMinWidth(maxLineNumber);
|
||||
</script>
|
||||
|
||||
<div class="hunks">
|
||||
{#if file.binary}
|
||||
{#if isBinary}
|
||||
Binary content not shown
|
||||
{:else if file.large}
|
||||
{:else if isLarge}
|
||||
Diff too large to be shown
|
||||
{:else}
|
||||
{#each sections as section}
|
||||
@ -39,7 +51,7 @@
|
||||
{/if}
|
||||
</div>
|
||||
<HunkViewer
|
||||
{file}
|
||||
{filePath}
|
||||
{section}
|
||||
{branchId}
|
||||
{selectable}
|
||||
@ -55,3 +67,31 @@
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="postcss">
|
||||
.hunks {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
max-height: 100%;
|
||||
flex-shrink: 0;
|
||||
padding: var(--space-16);
|
||||
gap: var(--space-16);
|
||||
}
|
||||
.hunk-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-10);
|
||||
}
|
||||
.indicators {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
}
|
||||
.added {
|
||||
color: #45b156;
|
||||
}
|
||||
.removed {
|
||||
color: #ff3e00;
|
||||
}
|
||||
</style>
|
||||
|
@ -5,9 +5,8 @@
|
||||
import ContextMenuSection from '$lib/components/contextmenu/ContextMenuSection.svelte';
|
||||
import { open } from '@tauri-apps/api/shell';
|
||||
import type { BranchController } from '$lib/vbranches/branchController';
|
||||
import type { File } from '$lib/vbranches/types';
|
||||
|
||||
export let file: File;
|
||||
export let filePath: string;
|
||||
export let projectPath: string | undefined;
|
||||
export let branchController: BranchController;
|
||||
let popupMenu: PopupMenu;
|
||||
@ -27,7 +26,7 @@
|
||||
<ContextMenuItem
|
||||
label="Open in VS Code"
|
||||
on:click={() =>
|
||||
projectPath && open(`vscode://file${projectPath}/${file.path}:${item.lineNumber}`)}
|
||||
projectPath && open(`vscode://file${projectPath}/${filePath}:${item.lineNumber}`)}
|
||||
/>
|
||||
{/if}
|
||||
</ContextMenuSection>
|
||||
|
@ -81,6 +81,7 @@
|
||||
.line {
|
||||
flex-grow: 1;
|
||||
cursor: grab;
|
||||
padding-left: var(--space-4);
|
||||
}
|
||||
|
||||
.code-line__numbers-line {
|
||||
@ -92,6 +93,5 @@
|
||||
.selectable-wrapper {
|
||||
cursor: text;
|
||||
display: inline-block;
|
||||
padding-left: var(--space-4);
|
||||
}
|
||||
</style>
|
||||
|
@ -7,13 +7,12 @@
|
||||
import type { HunkSection } from '$lib/utils/fileSections';
|
||||
import type { BranchController } from '$lib/vbranches/branchController';
|
||||
import type { Ownership } from '$lib/vbranches/ownership';
|
||||
import type { File } from '$lib/vbranches/types';
|
||||
import type { Hunk } from '$lib/vbranches/types';
|
||||
import type { Writable } from 'svelte/store';
|
||||
|
||||
export let file: File;
|
||||
export let filePath: string;
|
||||
export let section: HunkSection;
|
||||
export let branchId: string;
|
||||
export let branchId: string | undefined;
|
||||
export let projectPath: string | undefined;
|
||||
|
||||
export let minWidth: number;
|
||||
@ -22,9 +21,10 @@
|
||||
export let isFileLocked: boolean;
|
||||
|
||||
export let branchController: BranchController;
|
||||
export let selectedOwnership: Writable<Ownership>;
|
||||
export let selectedOwnership: Writable<Ownership> | undefined = undefined;
|
||||
|
||||
function onHunkSelected(hunk: Hunk, isSelected: boolean) {
|
||||
if (!selectedOwnership) return;
|
||||
if (isSelected) {
|
||||
selectedOwnership.update((ownership) => ownership.addHunk(hunk.filePath, hunk.id));
|
||||
} else {
|
||||
@ -32,15 +32,15 @@
|
||||
}
|
||||
}
|
||||
|
||||
function updateContextMenu(file: File) {
|
||||
function updateContextMenu(filePath: string) {
|
||||
if (popupMenu) popupMenu.$destroy();
|
||||
return new HunkContextMenu({
|
||||
target: document.body,
|
||||
props: { projectPath, file, branchController }
|
||||
props: { projectPath, filePath, branchController }
|
||||
});
|
||||
}
|
||||
|
||||
$: popupMenu = updateContextMenu(file);
|
||||
$: popupMenu = updateContextMenu(filePath);
|
||||
|
||||
onDestroy(() => {
|
||||
if (popupMenu) {
|
||||
@ -54,7 +54,7 @@
|
||||
role="cell"
|
||||
use:draggable={{
|
||||
...draggableHunk(branchId, section.hunk),
|
||||
disabled: isUnapplied || section.hunk.locked
|
||||
disabled: isUnapplied || section.hunk.locked || !branchId
|
||||
}}
|
||||
on:contextmenu|preventDefault
|
||||
class="hunk"
|
||||
@ -66,12 +66,12 @@
|
||||
{#each subsection.lines.slice(0, subsection.expanded ? subsection.lines.length : 0) as line}
|
||||
<HunkLine
|
||||
{line}
|
||||
{filePath}
|
||||
{minWidth}
|
||||
{selectable}
|
||||
selected={$selectedOwnership.containsHunk(hunk.filePath, hunk.id)}
|
||||
selected={$selectedOwnership?.containsHunk(hunk.filePath, hunk.id)}
|
||||
on:selected={(e) => onHunkSelected(hunk, e.detail)}
|
||||
sectionType={subsection.sectionType}
|
||||
filePath={file.path}
|
||||
on:contextmenu={(e) =>
|
||||
popupMenu.openByMouse(e, {
|
||||
hunk,
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
export let branch: RemoteBranch | undefined;
|
||||
export let projectId: string;
|
||||
export let projectPath: string;
|
||||
export let branchController: BranchController;
|
||||
</script>
|
||||
|
||||
@ -23,7 +24,7 @@
|
||||
{#if branch.commits && branch.commits.length > 0}
|
||||
<div class="flex w-full flex-col gap-y-2">
|
||||
{#each branch.commits as commit}
|
||||
<CommitCard {commit} {projectId} />
|
||||
<CommitCard {commit} {projectId} {branchController} {projectPath} />
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
export let branchId: string;
|
||||
export let projectId: string;
|
||||
export let projectPath: string;
|
||||
export let branchCount: number;
|
||||
export let upstream: RemoteBranch | undefined;
|
||||
export let branchController: BranchController;
|
||||
@ -53,7 +54,13 @@
|
||||
>
|
||||
{#each upstream.commits as commit (commit.id)}
|
||||
<div use:draggable={draggableRemoteCommit(branchId, commit)}>
|
||||
<CommitCard {commit} {projectId} commitUrl={base?.commitUrl(commit.id)} />
|
||||
<CommitCard
|
||||
{commit}
|
||||
{projectId}
|
||||
commitUrl={base?.commitUrl(commit.id)}
|
||||
{branchController}
|
||||
{projectPath}
|
||||
/>
|
||||
</div>
|
||||
{/each}
|
||||
<div class="flex justify-end p-2">
|
||||
|
@ -13,7 +13,7 @@ export type DraggableHunk = {
|
||||
hunk: Hunk;
|
||||
};
|
||||
|
||||
export function draggableHunk(branchId: string, hunk: Hunk) {
|
||||
export function draggableHunk(branchId: string | undefined, hunk: Hunk) {
|
||||
return { data: { branchId, hunk } };
|
||||
}
|
||||
|
||||
|
19
gitbutler-ui/src/lib/vbranches/remoteCommits.ts
Normal file
19
gitbutler-ui/src/lib/vbranches/remoteCommits.ts
Normal file
@ -0,0 +1,19 @@
|
||||
/**
|
||||
* This file should probably not be located under ../vbranches, but the reason
|
||||
* it's here is because the type is in this package.
|
||||
*/
|
||||
import { RemoteFile } from './types';
|
||||
import { parseFileSections, type ContentSection, type HunkSection } from '$lib/utils/fileSections';
|
||||
import { invoke } from '@tauri-apps/api/tauri';
|
||||
import { plainToInstance } from 'class-transformer';
|
||||
|
||||
export async function listRemoteCommitFiles(projectId: string, commitOid: string) {
|
||||
return plainToInstance(
|
||||
RemoteFile,
|
||||
await invoke<any[]>('list_remote_commit_files', { projectId, commitOid })
|
||||
)
|
||||
.map(
|
||||
(file) => [file, parseFileSections(file)] as [RemoteFile, (ContentSection | HunkSection)[]]
|
||||
)
|
||||
.sort((a, b) => a[0].path?.localeCompare(b[0].path));
|
||||
}
|
@ -9,6 +9,7 @@
|
||||
$: baseBranchService = data.baseBranchService;
|
||||
$: base$ = baseBranchService.base$;
|
||||
$: error$ = baseBranchService.error$;
|
||||
$: project$ = data.project$;
|
||||
</script>
|
||||
|
||||
<ScrollableContainer wide>
|
||||
@ -18,7 +19,7 @@
|
||||
{:else if !$base$}
|
||||
<p>Loading...</p>
|
||||
{:else}
|
||||
<BaseBranch {projectId} base={$base$} {branchController} />
|
||||
<BaseBranch {projectId} base={$base$} {branchController} projectPath={$project$.path} />
|
||||
{/if}
|
||||
</div>
|
||||
</ScrollableContainer>
|
||||
|
@ -4,7 +4,7 @@
|
||||
import { page } from '$app/stores';
|
||||
|
||||
export let data: PageData;
|
||||
$: projectId = data.projectId;
|
||||
$: project$ = data.project$;
|
||||
$: branchController = data.branchController;
|
||||
$: remoteBranchService = data.remoteBranchService;
|
||||
$: branches$ = remoteBranchService.branches$;
|
||||
@ -24,7 +24,12 @@
|
||||
{:else if !$branches$}
|
||||
<p>Loading...</p>
|
||||
{:else if branch}
|
||||
<RemoteBranchPreview {projectId} {branchController} {branch} />
|
||||
<RemoteBranchPreview
|
||||
projectId={$project$.id}
|
||||
projectPath={$project$.path}
|
||||
{branchController}
|
||||
{branch}
|
||||
/>
|
||||
{:else}
|
||||
<p>Branch doesn't seem to exist</p>
|
||||
{/if}
|
||||
|
Loading…
Reference in New Issue
Block a user