Radish alpha
r
Radicle web interface
Radicle
Git (anonymous pull)
Log in to clone via SSH
Update components and interfaces for radicle-surf v0.21
Sebastian Martinez committed 2 years ago
commit 7adcea414876b636635b8e0a3027b89d53e4572d
parent d413650ebcb450f87972396b7da9706ae1888725
13 files changed +110 -176
modified httpd-client/index.ts
@@ -19,6 +19,8 @@ import type {
  Commit,
  CommitBlob,
  CommitHeader,
+
  ChangesetWithDiff,
+
  ChangesetWithoutDiff,
  Diff,
  DiffBlob,
  DiffContent,
@@ -50,6 +52,8 @@ import { nodeConfigSchema, successResponseSchema } from "./lib/shared.js";
export type {
  BaseUrl,
  Blob,
+
  ChangesetWithDiff,
+
  ChangesetWithoutDiff,
  CodeLocation,
  Comment,
  Commit,
modified httpd-client/lib/project/commit.ts
@@ -6,6 +6,8 @@ export type {
  Diff,
  DiffContent,
  DiffFile,
+
  ChangesetWithDiff,
+
  ChangesetWithoutDiff,
  HunkLine,
  Hunks,
};
@@ -13,6 +15,7 @@ export type {
import {
  array,
  boolean,
+
  discriminatedUnion,
  literal,
  number,
  object,
@@ -115,35 +118,47 @@ const changesetHunkSchema = object({

type DiffContent = z.infer<typeof diffContentSchema>;

-
const diffContentSchema = object({
-
  type: union([literal("plain"), literal("binary"), literal("empty")]),
-
  hunks: array(changesetHunkSchema),
-
  eof: union([
-
    literal("noneMissing"),
-
    literal("oldMissing"),
-
    literal("newMissing"),
-
    literal("bothMissing"),
-
  ]),
-
});
-

+
const diffContentSchema = discriminatedUnion("type", [
+
  object({
+
    type: literal("plain"),
+
    stats: object({
+
      additions: number(),
+
      deletions: number(),
+
    }),
+
    hunks: array(changesetHunkSchema),
+
    eof: union([
+
      literal("noneMissing"),
+
      literal("oldMissing"),
+
      literal("newMissing"),
+
      literal("bothMissing"),
+
    ]),
+
  }),
+
  object({ type: literal("binary") }),
+
  object({ type: literal("empty") }),
+
]);
const diffChangesetSchema = object({
  path: string(),
  diff: diffContentSchema,
});

const diffAddedChangesetSchema = diffChangesetSchema.merge(
-
  object({ new: diffFileSchema }),
+
  object({ state: literal("added"), new: diffFileSchema }),
);

const diffDeletedChangesetSchema = diffChangesetSchema.merge(
-
  object({ old: diffFileSchema }),
+
  object({ state: literal("deleted"), old: diffFileSchema }),
);

const diffModifiedChangesetSchema = diffChangesetSchema.merge(
-
  object({ new: diffFileSchema, old: diffFileSchema }),
+
  object({
+
    state: literal("modified"),
+
    new: diffFileSchema,
+
    old: diffFileSchema,
+
  }),
);

const diffCopiedChangesetSchema = object({
+
  state: literal("copied"),
  newPath: string(),
  oldPath: string(),
});
@@ -158,6 +173,7 @@ const diffCopiedWithModificationsChangesetSchema =
  );

const diffMovedChangesetSchema = object({
+
  state: literal("moved"),
  newPath: string(),
  oldPath: string(),
  current: diffFileSchema,
@@ -175,20 +191,31 @@ const diffMovedWithModificationsChangesetSchema = diffMovedChangesetSchema

type Diff = z.infer<typeof diffSchema>;

+
type ChangesetWithDiff = z.infer<typeof changesetWithDiffSchema>;
+
type ChangesetWithoutDiff = z.infer<typeof changesetWithoutDiffSchema>;
+

+
const changesetWithDiffSchema = union([
+
  diffAddedChangesetSchema,
+
  diffDeletedChangesetSchema,
+
  diffModifiedChangesetSchema,
+
  diffMovedWithModificationsChangesetSchema,
+
  diffCopiedWithModificationsChangesetSchema,
+
]);
+
const changesetWithoutDiffSchema = union([
+
  diffMovedChangesetSchema,
+
  diffCopiedChangesetSchema,
+
]);
+

const diffSchema = object({
-
  added: array(diffAddedChangesetSchema),
-
  deleted: array(diffDeletedChangesetSchema),
-
  modified: array(diffModifiedChangesetSchema),
-
  moved: array(
+
  files: array(
    union([
+
      diffAddedChangesetSchema,
+
      diffDeletedChangesetSchema,
+
      diffModifiedChangesetSchema,
      diffMovedChangesetSchema,
      diffMovedWithModificationsChangesetSchema,
-
    ]),
-
  ),
-
  copied: array(
-
    union([
-
      diffCopiedWithModificationsChangesetSchema,
      diffCopiedChangesetSchema,
+
      diffCopiedWithModificationsChangesetSchema,
    ]),
  ),
  stats: object({
@@ -210,7 +237,7 @@ const commitSchema = object({
type Commits = z.infer<typeof commitsSchema>;

const commitsSchema = object({
-
  commits: array(commitSchema.omit({ files: true })),
+
  commits: array(commitHeaderSchema),
  stats: object({
    commits: number(),
    branches: number(),
modified src/views/projects/Changeset.svelte
@@ -1,11 +1,5 @@
<script lang="ts">
-
  import type {
-
    BaseUrl,
-
    CommitBlob,
-
    Diff,
-
    DiffContent,
-
    DiffFile,
-
  } from "@httpd-client";
+
  import type { BaseUrl, CommitBlob, Diff } from "@httpd-client";

  import FileDiff from "@app/views/projects/Changeset/FileDiff.svelte";
  import FileLocationChange from "@app/views/projects/Changeset/FileLocationChange.svelte";
@@ -24,43 +18,19 @@
    return count === 1 ? singular : `${singular}s`;
  }

-
  const diffDescription = ({
-
    modified,
-
    added,
-
    deleted,
-
    moved,
-
    copied,
-
  }: Diff): string => {
-
    const s = [];
-

-
    if (modified.length) {
-
      s.push(
-
        `${modified.length} ${pluralize("file", modified.length)} changed`,
-
      );
-
    }
-
    if (added.length) {
-
      s.push(`${added.length} ${pluralize("file", added.length)} added`);
-
    }
-
    if (deleted.length) {
-
      s.push(`${deleted.length} ${pluralize("file", deleted.length)} deleted`);
-
    }
-
    if (copied.length) {
-
      s.push(`${copied.length} ${pluralize("file", copied.length)} copied`);
-
    }
-
    if (moved.length) {
-
      s.push(`${moved.length} ${pluralize("file", moved.length)} moved`);
-
    }
-
    return s.join(", ");
-
  };
-

-
  // Since libgit2 optimizes around not loading the content, when no content callbacks are configured,
-
  // and we don't want to loop over all diffs in radicle-surf to get a correct answer.
-
  // We assume that a `blobExecutable` file with no hunks is a binary file.
-
  function getFileType(diff: DiffContent, file: DiffFile): DiffContent["type"] {
-
    return file.mode === "blobExecutable" && diff.hunks.length === 0
-
      ? "binary"
-
      : diff.type;
-
  }
+
  const diffDescription = (diffFiles: Diff["files"]): string =>
+
    Object.entries(
+
      diffFiles.reduce(
+
        (acc, { state }) => {
+
          acc[state]++;
+
          return acc;
+
        },
+
        { added: 0, modified: 0, deleted: 0, copied: 0, moved: 0 },
+
      ),
+
    )
+
      .filter(([, count]) => count > 0)
+
      .map(([state, count]) => `${count} ${pluralize("file", count)} ${state}`)
+
      .join(", ");
</script>

<style>
@@ -92,7 +62,7 @@

<div class="header">
  <div class="summary">
-
    <span>{diffDescription(diff)}</span>
+
    <span>{diffDescription(diff.files)}</span>
    with
    <span class:additions={diff.stats.insertions > 0}>
      {diff.stats.insertions}
@@ -119,99 +89,34 @@

<div class="diff-list">
  <Observer let:filesVisibility let:observer>
-
    {#each diff.added as file}
-
      <div use:intersection={observer} id={"observer:" + file.path}>
-
        <FileDiff
-
          {projectId}
-
          {baseUrl}
-
          {revision}
-
          {expanded}
-
          visible={filesVisibility.has(file.path)}
-
          content={files[file.new.oid]?.content}
-
          filePath={file.path}
-
          fileDiff={{ ...file.diff, type: getFileType(file.diff, file.new) }}
-
          headerBadgeCaption="added" />
-
      </div>
-
    {/each}
-
    {#each diff.deleted as file}
-
      <div use:intersection={observer} id={"observer:" + file.path}>
-
        <FileDiff
-
          {projectId}
-
          {baseUrl}
-
          {revision}
-
          {expanded}
-
          visible={filesVisibility.has(file.path)}
-
          oldContent={files[file.old.oid]?.content}
-
          filePath={file.path}
-
          fileDiff={{ ...file.diff, type: getFileType(file.diff, file.old) }}
-
          headerBadgeCaption="deleted" />
-
      </div>
-
    {/each}
-
    {#each diff.modified as file}
-
      <div use:intersection={observer} id={"observer:" + file.path}>
-
        <FileDiff
-
          {projectId}
-
          {baseUrl}
-
          {revision}
-
          {expanded}
-
          visible={filesVisibility.has(file.path)}
-
          oldContent={files[file.old.oid]?.content}
-
          content={files[file.new.oid]?.content}
-
          filePath={file.path}
-
          fileDiff={{ ...file.diff, type: getFileType(file.diff, file.new) }} />
-
      </div>
-
    {/each}
-
    {#each diff.moved as file}
-
      {#if "diff" in file}
-
        <div use:intersection={observer} id={"observer:" + file.newPath}>
+
    {#each diff.files as file}
+
      {@const path = "path" in file ? file.path : file.newPath}
+
      <div use:intersection={observer} id={"observer:" + path}>
+
        {#if "diff" in file}
          <FileDiff
            {projectId}
            {baseUrl}
            {revision}
            {expanded}
-
            content={files[file.new.oid]?.content}
-
            oldContent={files[file.old.oid]?.content}
-
            visible={filesVisibility.has(file.newPath)}
-
            filePath={file.newPath}
-
            oldFilePath={file.oldPath}
-
            fileDiff={{ ...file.diff, type: getFileType(file.diff, file.new) }}
-
            headerBadgeCaption="moved" />
-
        </div>
-
      {:else}
-
        <FileLocationChange
-
          {projectId}
-
          {baseUrl}
-
          {revision}
-
          newPath={file.newPath}
-
          oldPath={file.oldPath}
-
          mode="moved" />
-
      {/if}
-
    {/each}
-
    {#each diff.copied as file}
-
      {#if "diff" in file}
-
        <div use:intersection={observer} id={"observer:" + file.newPath}>
-
          <FileDiff
+
            filePath={path}
+
            oldFilePath={"oldPath" in file ? file.oldPath : undefined}
+
            fileDiff={file.diff}
+
            headerBadgeCaption={file.state}
+
            content={"new" in file ? files[file.new.oid]?.content : undefined}
+
            oldContent={"old" in file
+
              ? files[file.old.oid]?.content
+
              : undefined}
+
            visible={filesVisibility.has(path)} />
+
        {:else}
+
          <FileLocationChange
+
            headerBadgeCaption={file.state}
+
            oldPath={file.oldPath}
+
            newPath={file.newPath}
            {projectId}
            {baseUrl}
-
            {revision}
-
            {expanded}
-
            content={files[file.new.oid]?.content}
-
            oldContent={files[file.old.oid]?.content}
-
            visible={filesVisibility.has(file.newPath)}
-
            filePath={file.newPath}
-
            oldFilePath={file.oldPath}
-
            fileDiff={{ ...file.diff, type: getFileType(file.diff, file.new) }}
-
            headerBadgeCaption="copied" />
-
        </div>
-
      {:else}
-
        <FileLocationChange
-
          {projectId}
-
          {baseUrl}
-
          {revision}
-
          newPath={file.newPath}
-
          oldPath={file.oldPath}
-
          mode="copied" />
-
      {/if}
+
            {revision} />
+
        {/if}
+
      </div>
    {/each}
  </Observer>
</div>
modified src/views/projects/Changeset/FileDiff.svelte
@@ -1,5 +1,10 @@
<script lang="ts">
-
  import type { BaseUrl, DiffContent, HunkLine } from "@httpd-client";
+
  import type {
+
    BaseUrl,
+
    ChangesetWithDiff,
+
    DiffContent,
+
    HunkLine,
+
  } from "@httpd-client";

  import { onDestroy, onMount } from "svelte";
  import { toHtml } from "hast-util-to-html";
@@ -23,13 +28,8 @@
  export let content: string | undefined = undefined;
  export let oldFilePath: string | undefined = undefined;
  export let fileDiff: DiffContent;
+
  export let headerBadgeCaption: ChangesetWithDiff["state"];
  export let revision: string | undefined = undefined;
-
  export let headerBadgeCaption:
-
    | "added"
-
    | "deleted"
-
    | "moved"
-
    | "copied"
-
    | undefined = undefined;
  export let baseUrl: BaseUrl;
  export let projectId: string;
  export let visible: boolean = false;
modified src/views/projects/Changeset/FileLocationChange.svelte
@@ -1,5 +1,5 @@
<script lang="ts">
-
  import type { BaseUrl } from "@httpd-client";
+
  import type { BaseUrl, ChangesetWithoutDiff } from "@httpd-client";

  import Badge from "@app/components/Badge.svelte";
  import IconButton from "@app/components/IconButton.svelte";
@@ -7,10 +7,10 @@
  import Link from "@app/components/Link.svelte";
  import FilePath from "@app/components/FilePath.svelte";

+
  export let headerBadgeCaption: ChangesetWithoutDiff["state"];
  export let newPath: string;
  export let oldPath: string;
  export let revision: string | undefined = undefined;
-
  export let mode: "moved" | "copied";
  export let baseUrl: BaseUrl;
  export let projectId: string;
</script>
@@ -45,9 +45,9 @@
        <FilePath filenameWithPath={oldPath} /> → <FilePath
          filenameWithPath={newPath} />
      </span>
-
      {#if mode === "moved"}
+
      {#if headerBadgeCaption === "moved"}
        <Badge variant="foreground">moved</Badge>
-
      {:else if mode === "copied"}
+
      {:else if headerBadgeCaption === "copied"}
        <Badge variant="foreground">copied</Badge>
      {/if}
    </div>
modified src/views/projects/History.svelte
@@ -54,10 +54,7 @@
        page,
        perPage: COMMITS_PER_PAGE,
      });
-
      allCommitHeaders = [
-
        ...allCommitHeaders,
-
        ...response.commits.map(c => c.commit),
-
      ];
+
      allCommitHeaders = response.commits;
      totalCommitCount = response.stats.commits;
    } catch (e) {
      error = e;
modified src/views/projects/router.ts
@@ -553,7 +553,7 @@ async function loadHistoryView(
      branches: Object.keys(branchMap || {}),
      revision: route.revision,
      tree,
-
      commitHeaders: commitsResponse.commits.map(c => c.commit),
+
      commitHeaders: commitsResponse.commits,
      totalCommitCount: commitsResponse.stats.commits,
      seeding,
    },
modified tests/e2e/node.spec.ts
@@ -19,7 +19,7 @@ test("node metadata", async ({ page, peerManager }) => {
  await expect(
    page.getByText(`${shortNodeRemote}@seed.radicle.test:8123`),
  ).toBeVisible();
-
  await expect(page.getByText("1.0.0-rc.1-")).toBeVisible();
+
  await expect(page.getByText("1.0.0-rc.5-")).toBeVisible();
  await peer.stopHttpd();
  await peer.stopNode();
});
modified tests/e2e/project.spec.ts
@@ -526,7 +526,7 @@ test("diff selection de-select", async ({ page }) => {
  await expect(page).toHaveURL(new RegExp("tab=changes#README.md:H0L1$"));
  // Click outside.
  await page
-
    .getByText("1 file changed with 5 insertions and 1 deletion")
+
    .getByText("1 file modified with 5 insertions and 1 deletion")
    .click();
  await expect(page).toHaveURL(new RegExp("tab=changes$"));
});
modified tests/e2e/project/commit.spec.ts
@@ -48,7 +48,7 @@ test("modified file", async ({ page }) => {

  // Diff header.
  await expect(
-
    page.getByText("1 file changed with 1 insertion and 4 deletions"),
+
    page.getByText("1 file modified with 1 insertion and 4 deletions"),
  ).toBeVisible();

  // Diff.
modified tests/support/fixtures.ts
@@ -38,6 +38,7 @@ export const test = base.extend<{
        if (
          msg.text().startsWith("[vite] connected.") ||
          msg.text().startsWith("[vite] connecting...") ||
+
          msg.text().startsWith("Not able to parse url") ||
          msg
            .text()
            .includes("Please make sure it wasn't preloaded for nothing.") ||
modified tests/support/heartwood-version
@@ -1 +1 @@
-
081af03362b5bd3d637ee22011a4e5b51a1f1498
+
c607619683e859fd715a23377c03fb08dc8090f4
modified tests/visual/desktop/cob.spec.ts
@@ -82,7 +82,7 @@ test("patch page", async ({ page }) => {
  await expect(page).toHaveScreenshot({ fullPage: true });
  await page.getByRole("button", { name: "Changes" }).click();
  await page
-
    .getByText("1 file changed with 5 insertions and 1 deletion")
+
    .getByText("1 file modified with 5 insertions and 1 deletion")
    .waitFor();
  await expect(page).toHaveScreenshot({ fullPage: true });
});