mirror of
https://seed.radicle.dev/rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5.git
synced 2026-11-02 18:20:29 +08:00
Redesign the entire app with a new UI kit. Typography, colors, icons, avatars, and border radii have all been updated for a more polished, consistent appearance that matches our other products like radicle-explorer. - Replace blockies-based avatars with a new DID-based avatar system - Move Settings and Create Issue into fullscreen modals - Change the default theme to follow the system theme - Remove window decorations on macOS for a cleaner, more native feel - Update the app icon - Fix popovers and tooltips Navigation: - Rewrite app navigation and layouts - Add a sidebar that gives you access to all your repos - Change the startup screen to open directly to the notification inbox - Add a getting started guide shown to new users on first launch Settings: - Add a setting to disable the notification badge - Add a code font setting Bug fixes: - Fix reactions and edits incorrectly targeting the root comment instead of the intended reply - Restrict issue/patch state, labels and assignees to delegates only
151 lines
4.1 KiB
Svelte
151 lines
4.1 KiB
Svelte
<script lang="ts">
|
|
import type { CodeComments } from "@app/components/Diff.svelte";
|
|
import type { FileDiff } from "@bindings/diff/FileDiff";
|
|
|
|
import Diff from "@app/components/Diff.svelte";
|
|
import FileBlock from "@app/components/FileBlock.svelte";
|
|
import Icon from "@app/components/Icon.svelte";
|
|
import Path from "@app/components/Path.svelte";
|
|
|
|
interface Props {
|
|
expanded: boolean;
|
|
file: FileDiff;
|
|
head: string;
|
|
codeComments?: CodeComments;
|
|
}
|
|
|
|
const { expanded, file, head, codeComments }: Props = $props();
|
|
|
|
// Pass down only the comments that apply to the given diff.
|
|
function filterThreadsByFilePath() {
|
|
if (codeComments) {
|
|
if (
|
|
file.status === "added" ||
|
|
file.status === "deleted" ||
|
|
file.status === "modified"
|
|
) {
|
|
return {
|
|
...codeComments,
|
|
threads:
|
|
codeComments.threads.filter(t => {
|
|
return t.root.location?.path === file.path;
|
|
}) ?? [],
|
|
};
|
|
} else {
|
|
return { ...codeComments, threads: [] };
|
|
}
|
|
} else {
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
const commentsOfThisFile = $derived(filterThreadsByFilePath());
|
|
const resolvedThreads = $derived(
|
|
commentsOfThisFile
|
|
? commentsOfThisFile.threads.filter(t => {
|
|
return t.root.resolved === true;
|
|
}).length
|
|
: 0,
|
|
);
|
|
const unresolvedThreads = $derived(
|
|
commentsOfThisFile
|
|
? commentsOfThisFile.threads.filter(t => {
|
|
return t.root.resolved === false;
|
|
}).length
|
|
: 0,
|
|
);
|
|
</script>
|
|
|
|
<style>
|
|
.added {
|
|
color: var(--color-feedback-success-text);
|
|
background-color: var(--color-feedback-success-bg);
|
|
}
|
|
.deleted {
|
|
color: var(--color-feedback-error-text);
|
|
background-color: var(--color-feedback-error-bg);
|
|
}
|
|
.moved,
|
|
.copied {
|
|
color: var(--color-text-secondary);
|
|
background: var(--color-surface-subtle);
|
|
}
|
|
.stats {
|
|
font: var(--txt-code-regular);
|
|
}
|
|
</style>
|
|
|
|
{#snippet emptyPlaceholder()}
|
|
<div class="global-flex" style:margin="1rem 0" style:justify-content="center">
|
|
Empty file
|
|
</div>
|
|
{/snippet}
|
|
|
|
<FileBlock {expanded}>
|
|
{#snippet leftHeader()}
|
|
{#if file.status === "moved" || file.status === "copied"}
|
|
<span style="display: flex; align-items: center; flex-wrap: wrap;">
|
|
<Path fullPath={file.oldPath} />
|
|
<span style:padding="0 0.5rem">→</span>
|
|
<Path fullPath={file.newPath} />
|
|
</span>
|
|
{:else}
|
|
<Path fullPath={file.path} />
|
|
{/if}
|
|
|
|
{#if file.status === "added"}
|
|
<span class="global-chip added">Added</span>
|
|
{:else if file.status === "deleted"}
|
|
<span class="global-chip deleted">Deleted</span>
|
|
{:else if file.status === "moved"}
|
|
<span class="global-chip moved">Moved</span>
|
|
{:else if file.status === "copied"}
|
|
<span class="global-chip copied">Copied</span>
|
|
{/if}
|
|
{/snippet}
|
|
|
|
{#snippet rightHeader()}
|
|
{#if file.diff.type === "plain" && file.diff.hunks.length > 0}
|
|
<div class="stats">
|
|
<span style:color="var(--color-feedback-success-text)">
|
|
+{file.diff.stats.additions}
|
|
</span>
|
|
<span style:color="var(--color-feedback-error-text)">
|
|
-{file.diff.stats.deletions}
|
|
</span>
|
|
</div>
|
|
{/if}
|
|
{#if commentsOfThisFile && commentsOfThisFile.threads.length > 0}
|
|
{#if unresolvedThreads > 0}
|
|
<div class="global-flex">
|
|
<Icon name="comment-cross" />
|
|
{unresolvedThreads}
|
|
</div>
|
|
{/if}
|
|
{#if resolvedThreads > 0}
|
|
<div class="global-flex">
|
|
<Icon name="comment-checkmark" />
|
|
{resolvedThreads}
|
|
</div>
|
|
{/if}
|
|
{/if}
|
|
{/snippet}
|
|
|
|
{#if file.diff.type === "plain"}
|
|
{#if file.diff.hunks.length > 0}
|
|
<Diff {file} {head} codeComments={commentsOfThisFile} />
|
|
{:else}
|
|
{@render emptyPlaceholder()}
|
|
{/if}
|
|
{:else if file.diff.type === "binary"}
|
|
<div
|
|
class="global-flex"
|
|
style:margin="1rem 0"
|
|
style:justify-content="center">
|
|
<Icon name="binary" />Binary file
|
|
</div>
|
|
{:else}
|
|
{@render emptyPlaceholder()}
|
|
{/if}
|
|
</FileBlock>
|