mirror of
https://seed.radicle.dev/rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5.git
synced 2026-11-15 11:20:36 +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
211 lines
6.2 KiB
Svelte
211 lines
6.2 KiB
Svelte
<script lang="ts">
|
|
import type { CodeComments } from "@app/components/Diff.svelte";
|
|
import type { Revision } from "@bindings/cob/patch/Revision";
|
|
|
|
import { cachedGetDiff, cachedListCommits } from "@app/lib/invoke";
|
|
import { pluralize } from "@app/lib/utils";
|
|
|
|
import Button from "@app/components/Button.svelte";
|
|
import Changeset from "@app/components/Changeset.svelte";
|
|
import CobCommitTeaser from "@app/components/CobCommitTeaser.svelte";
|
|
import CommitsContainer from "@app/components/CommitsContainer.svelte";
|
|
import Icon from "@app/components/Icon.svelte";
|
|
import Id from "@app/components/Id.svelte";
|
|
import JobCob from "@app/components/JobCob.svelte";
|
|
|
|
interface Props {
|
|
patchId: string;
|
|
revision: Revision;
|
|
rid: string;
|
|
codeComments?: CodeComments;
|
|
}
|
|
|
|
const { patchId, revision, rid, codeComments }: Props = $props();
|
|
|
|
let hideChanges = $state(false);
|
|
let filesExpanded = $state(true);
|
|
let selectedCommit = $state<string>();
|
|
let base = $state(revision.base);
|
|
let head = $state(revision.head);
|
|
|
|
$effect(() => {
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
patchId;
|
|
|
|
hideChanges = false;
|
|
filesExpanded = true;
|
|
selectedCommit = undefined;
|
|
base = revision.base;
|
|
head = revision.head;
|
|
});
|
|
|
|
function selectRevision({
|
|
headId,
|
|
baseId,
|
|
commitId = undefined,
|
|
showFiles = true,
|
|
}: {
|
|
headId: string;
|
|
baseId: string;
|
|
commitId?: string;
|
|
showFiles?: boolean;
|
|
}) {
|
|
head = headId;
|
|
base = baseId;
|
|
selectedCommit = commitId;
|
|
filesExpanded = showFiles;
|
|
}
|
|
|
|
const isActiveCommit = (commitId: string) => selectedCommit === commitId;
|
|
const isTeaserDisabled = (commitId: string) =>
|
|
selectedCommit ? selectedCommit !== commitId : false;
|
|
</script>
|
|
|
|
<style>
|
|
.commits {
|
|
position: relative;
|
|
display: flex;
|
|
flex-direction: column;
|
|
font: var(--txt-body-m-regular);
|
|
margin-left: 0.5rem;
|
|
gap: 0.5rem;
|
|
padding: 1rem 0.5rem 0.5rem 1rem;
|
|
border-left: 1px solid var(--color-border-subtle);
|
|
}
|
|
.commit:last-of-type::after {
|
|
content: "";
|
|
position: absolute;
|
|
left: -18.5px;
|
|
top: 14px;
|
|
bottom: -0.5rem;
|
|
border-left: 4px solid var(--color-surface-canvas);
|
|
}
|
|
.commit-dot {
|
|
width: 0.25rem;
|
|
height: 0.25rem;
|
|
position: absolute;
|
|
top: 0.625rem;
|
|
left: -18.5px;
|
|
background-color: var(--color-border-subtle);
|
|
}
|
|
.commit {
|
|
cursor: pointer;
|
|
}
|
|
.commit-dot.active {
|
|
background-color: var(--color-border-brand);
|
|
}
|
|
.commit:hover:not(.single-commit) .commit-dot:not(.active) {
|
|
background-color: var(--color-text-primary);
|
|
}
|
|
.commit:hover:not(.single-commit) {
|
|
background-color: var(--color-surface-canvas);
|
|
}
|
|
.disabled {
|
|
color: var(--color-text-disabled) !important;
|
|
}
|
|
.summary {
|
|
cursor: pointer;
|
|
padding: 0.25rem 0;
|
|
}
|
|
.summary:hover:not(.single-commit) {
|
|
background-color: var(--color-surface-canvas);
|
|
color: var(--color-text-primary) !important;
|
|
}
|
|
.single-commit {
|
|
cursor: default !important;
|
|
}
|
|
</style>
|
|
|
|
<div
|
|
class="txt-body-m-regular global-flex"
|
|
style:margin-bottom={hideChanges ? undefined : "1rem"}>
|
|
<div class="global-flex">
|
|
<Button variant="naked" onclick={() => (hideChanges = !hideChanges)}>
|
|
<Icon name={hideChanges ? "chevron-right" : "chevron-down"} />
|
|
</Button>
|
|
<div class="txt-body-m-regular global-flex">Changes</div>
|
|
</div>
|
|
{#if !hideChanges}
|
|
<div style:margin-left="auto">
|
|
<Button variant="naked" onclick={() => (filesExpanded = !filesExpanded)}>
|
|
{#if filesExpanded === true}
|
|
<Icon name="collapse-vertical" />
|
|
Collapse all
|
|
{:else}
|
|
<Icon name="expand-vertical" />
|
|
Expand all
|
|
{/if}
|
|
</Button>
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
|
|
<div style:display={hideChanges ? "none" : "revert"}>
|
|
{#await cachedListCommits(rid, revision.base, revision.head) then commits}
|
|
<div style:margin-bottom="1rem">
|
|
<CommitsContainer>
|
|
{#snippet leftHeader()}
|
|
<div class="txt-body-m-regular">Commits</div>
|
|
{/snippet}
|
|
<div style:padding="0 1rem">
|
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
<div
|
|
class="global-flex txt-body-m-regular summary"
|
|
class:single-commit={commits.length === 1}
|
|
class:disabled={selectedCommit}
|
|
onclick={() => {
|
|
if (commits.length === 1) return;
|
|
selectRevision({
|
|
headId: revision.head,
|
|
baseId: revision.base,
|
|
});
|
|
}}>
|
|
<Icon name="branch" />
|
|
{commits.length}
|
|
{pluralize("commit", commits.length)} on base
|
|
<Id id={revision.base} clipboard={revision.base} />
|
|
<div class="global-chip">Base</div>
|
|
</div>
|
|
<div class="commits">
|
|
{#each [...commits].reverse() as commit, idx}
|
|
<div
|
|
class="commit"
|
|
class:single-commit={commits.length === 1}
|
|
style:position="relative">
|
|
<div class="commit-dot"></div>
|
|
<div
|
|
class="commit-dot"
|
|
class:active={isActiveCommit(commit.id)}>
|
|
</div>
|
|
<CobCommitTeaser
|
|
hoverable={commits.length > 1}
|
|
disabled={isTeaserDisabled(commit.id)}
|
|
onclick={() => {
|
|
if (commits.length === 1) return;
|
|
selectRevision({
|
|
headId: commit.id,
|
|
baseId: commit.parents[0],
|
|
commitId: commit.id,
|
|
});
|
|
}}
|
|
{commit}>
|
|
{#if idx === commits.length - 1}
|
|
<JobCob {rid} commit={commit.id} />
|
|
{/if}
|
|
</CobCommitTeaser>
|
|
</div>
|
|
{/each}
|
|
</div>
|
|
</div>
|
|
</CommitsContainer>
|
|
</div>
|
|
{/await}
|
|
|
|
{#await cachedGetDiff(rid, { base, head, unified: 3, highlight: true })}
|
|
<span class="txt-body-m-regular">Loading…</span>
|
|
{:then diff}
|
|
<Changeset expanded={filesExpanded} {head} {diff} {codeComments} />
|
|
{/await}
|
|
</div>
|