Radish alpha
r
Radicle web interface
Radicle
Git (anonymous pull)
Log in to clone via SSH
Add editing of revisions
Sebastian Martinez committed 2 years ago
commit 4912d85198f833477a554f2e8fbd877359687d82
parent ecba24352654f05e8621ede2958c46ae4a225746
3 files changed +129 -28
modified src/views/projects/Cob/Revision.svelte
@@ -19,6 +19,7 @@
  import DropdownListItem from "@app/components/DropdownList/DropdownListItem.svelte";
  import ErrorMessage from "@app/components/ErrorMessage.svelte";
  import ExpandButton from "@app/components/ExpandButton.svelte";
+
  import ExtendedTextarea from "@app/components/ExtendedTextarea.svelte";
  import IconButton from "@app/components/IconButton.svelte";
  import IconSmall from "@app/components/IconSmall.svelte";
  import Link from "@app/components/Link.svelte";
@@ -46,7 +47,8 @@
  export let previousRevId: string | undefined = undefined;
  export let previousRevOid: string | undefined = undefined;
  export let first: boolean;
-
  export let canEditComment: (author: string) => true | undefined;
+
  export let canEdit: (author: string) => true | undefined;
+
  export let editRevision: ((description: string) => Promise<void>) | undefined;
  export let editComment:
    | ((commentId: string, body: string, embeds: Embed[]) => Promise<void>)
    | undefined;
@@ -95,9 +97,12 @@
    }
  }

+
  type State = "read" | "submit" | "edit";
+

  let response: DiffResponse | undefined = undefined;
  let error: any | undefined = undefined;
  let loading: boolean = false;
+
  let revisionState: State = "read";

  onMount(async () => {
    try {
@@ -194,7 +199,6 @@
    font-size: var(--font-size-small);
  }
  .timestamp {
-
    margin-left: auto;
    font-size: var(--font-size-small);
    color: var(--color-fill-gray);
  }
@@ -382,14 +386,39 @@
                {utils.formatObjectId(revisionId)}
              </span>
            {/if}
-

-
            <div
-
              class="timestamp"
-
              title={utils.absoluteTimestamp(revisionTimestamp)}>
-
              {utils.formatTimestamp(revisionTimestamp)}
+
            <div style="display: flex; gap: 0.5rem; margin-left: auto;">
+
              {#if canEdit(revisionAuthor.id) && editRevision && revisionState === "read"}
+
                <IconButton
+
                  title="edit revision"
+
                  on:click={() => (revisionState = "edit")}>
+
                  <IconSmall name="edit" />
+
                </IconButton>
+
              {/if}
+
              <div
+
                class="timestamp"
+
                title={utils.absoluteTimestamp(revisionTimestamp)}>
+
                {utils.formatTimestamp(revisionTimestamp)}
+
              </div>
            </div>
          </div>
-
          {#if revisionDescription && !first}
+
          {#if editRevision && revisionState !== "read"}
+
            {@const editRevision_ = editRevision}
+
            <ExtendedTextarea
+
              rawPath={rawPath(revisionId)}
+
              body={revisionDescription}
+
              submitCaption="Save"
+
              submitInProgress={revisionState === "submit"}
+
              placeholder="Leave a description"
+
              on:close={() => (revisionState = "read")}
+
              on:submit={async ({ detail: { comment } }) => {
+
                revisionState = "submit";
+
                try {
+
                  await editRevision_(comment);
+
                } finally {
+
                  revisionState = "read";
+
                }
+
              }} />
+
          {:else if revisionDescription && !first}
            <div class="revision-description txt-small">
              <Markdown
                rawPath={rawPath(projectHead)}
@@ -433,7 +462,7 @@
            enableAttachments
            thread={element.inner}
            rawPath={rawPath(projectHead)}
-
            {canEditComment}
+
            canEditComment={canEdit}
            {editComment}
            {createReply}
            {handleReaction} />
@@ -460,6 +489,7 @@
              </span>

              <div
+
                style="margin-left: auto"
                class="timestamp"
                title={utils.absoluteTimestamp(revisionTimestamp)}>
                {utils.formatTimestamp(revisionTimestamp)}
modified src/views/projects/Patch.svelte
@@ -93,26 +93,46 @@
    ["Convert to draft", { status: "draft" }],
  ];

-
  async function editPatch(
-
    sessionId: string,
-
    title: string,
-
    description: string,
-
  ) {
+
  async function editPatch(sessionId: string, title: string) {
    try {
      await api.project.updatePatch(
        project.id,
        patch.id,
-
        {
-
          type: "revision.edit",
-
          revision: patch.id,
-
          description,
-
        },
+
        { type: "edit", title, target: "delegates" },
        sessionId,
      );
+
    } catch (error) {
+
      if (error instanceof Error) {
+
        modal.show({
+
          component: ErrorModal,
+
          props: {
+
            title: "Patch title editing failed",
+
            subtitle: [
+
              "There was an error while updating the title of this patch.",
+
              "Check your radicle-httpd logs for details.",
+
            ],
+
            error: {
+
              message: error.message,
+
              stack: error.stack,
+
            },
+
          },
+
        });
+
      }
+
    } finally {
+
      await refreshPatch();
+
    }
+
  }
+

+
  async function editRevision(
+
    sessionId: string,
+
    revisionId: string,
+
    description: string,
+
  ) {
+
    try {
      await api.project.updatePatch(
        project.id,
        patch.id,
-
        { type: "edit", title, target: "delegates" },
+
        { type: "revision.edit", revision: revisionId, description },
        sessionId,
      );
    } catch (error) {
@@ -120,9 +140,9 @@
        modal.show({
          component: ErrorModal,
          props: {
-
            title: "Patch editing failed",
+
            title: "Revision editing failed",
            subtitle: [
-
              "There was an error while updating the patch.",
+
              "There was an error while updating the revision.",
              "Check your radicle-httpd logs for details.",
            ],
            error: {
@@ -693,15 +713,13 @@
                submitCaption="Save"
                submitInProgress={patchState === "submit"}
                placeholder="Leave a description"
-
                on:close={() => {
-
                  patchState = "read";
-
                  void refreshPatch();
-
                }}
+
                on:close={() => (patchState = "read")}
                on:submit={async ({ detail: { comment } }) => {
                  patchState = "submit";
                  if (session) {
                    try {
-
                      await editPatch(session.id, patch.title, comment);
+
                      await editPatch(session.id, patch.title);
+
                      await editRevision(session.id, patch.id, comment);
                    } finally {
                      patchState = "read";
                    }
@@ -850,11 +868,13 @@
            projectHead={project.head}
            {...revision}
            first={index === 0}
-
            canEditComment={partial(
+
            canEdit={partial(
              role.isDelegateOrAuthor,
              session?.publicKey,
              project.delegates,
            )}
+
            editRevision={session &&
+
              partial(editRevision, session.id, revision.revisionId)}
            editComment={session &&
              partial(editComment, session.id, revision.revisionId)}
            handleReaction={session &&
modified tests/e2e/project/patch.spec.ts
@@ -226,3 +226,54 @@ test("edit patch", async ({ page, authenticatedPeer }) => {
    page.getByText("This is a modified patch description"),
  ).toBeVisible();
});
+

+
test("edit revision", async ({ page, authenticatedPeer }) => {
+
  const { rid, projectFolder } = await createProject(authenticatedPeer, {
+
    name: "edit-revision",
+
  });
+
  await authenticatedPeer.git(["switch", "-c", "edit-revision"], {
+
    cwd: projectFolder,
+
  });
+
  await authenticatedPeer.git(
+
    [
+
      "commit",
+
      "--allow-empty",
+
      "-m",
+
      "Some patch title",
+
      "-m",
+
      "This should be a description",
+
    ],
+
    {
+
      cwd: projectFolder,
+
    },
+
  );
+
  const patchId = extractPatchId(
+
    await authenticatedPeer.git(["push", "rad", "HEAD:refs/patches"], {
+
      cwd: projectFolder,
+
    }),
+
  );
+
  await authenticatedPeer.git(
+
    [
+
      "commit",
+
      "--allow-empty",
+
      "-m",
+
      "Let's create a new revision",
+
      "-m",
+
      "More descriptions",
+
    ],
+
    {
+
      cwd: projectFolder,
+
    },
+
  );
+
  await authenticatedPeer.git(["push", "rad"], { cwd: projectFolder });
+

+
  await page.goto(`${authenticatedPeer.ridUrl(rid)}/patches/${patchId}`);
+
  await page.getByRole("button", { name: "edit revision" }).click();
+
  await page
+
    .getByPlaceholder("Leave a description")
+
    .fill("This is an edited second revision");
+
  await page.getByRole("button", { name: "Save" }).click();
+
  await expect(
+
    page.getByText("This is an edited second revision"),
+
  ).toBeVisible();
+
});