Radish alpha
r
Radicle desktop app
Radicle
Git (anonymous pull)
Log in to clone via SSH
Refactor discussion component
Rūdolfs Ošiņš committed 1 year ago
commit ba79a3d22f281b32219c676814e703191c07e201
parent 6c6484f2a36263eba56d5bbc1d1d6a696a42fb4f
4 files changed +141 -156
modified src/components/Discussion.svelte
@@ -1,19 +1,15 @@
<script lang="ts">
  import type { Author } from "@bindings/cob/Author";
-
  import type { CodeLocation } from "@bindings/cob/thread/CodeLocation";
-
  import type { Comment } from "@bindings/cob/thread/Comment";
  import type { Config } from "@bindings/config/Config";
  import type { Embed } from "@bindings/cob/thread/Embed";
  import type { Thread } from "@bindings/cob/thread/Thread";

-
  import { tick } from "svelte";
  import partial from "lodash/partial";
+
  import sum from "lodash/sum";
+
  import { tick } from "svelte";

  import * as roles from "@app/lib/roles";
-
  import { announce } from "@app/components/AnnounceSwitch.svelte";
-
  import { invoke } from "@app/lib/invoke";
-
  import { nodeRunning } from "@app/lib/events";
-
  import { publicKeyFromDid, scrollIntoView } from "@app/lib/utils";
+
  import { scrollIntoView } from "@app/lib/utils";

  import CommentToggleInput from "@app/components/CommentToggleInput.svelte";
  import Icon from "@app/components/Icon.svelte";
@@ -21,32 +17,47 @@
  import ThreadComponent from "@app/components/Thread.svelte";

  interface Props {
+
    cobId: string;
+
    commentThreads: Thread[];
    config: Config;
-
    discussion?: Array<Comment<CodeLocation>>;
-
    patchId: string;
-
    reload: () => Promise<void>;
+
    createComment: (
+
      body: string,
+
      embeds: Embed[],
+
      replyTo?: string,
+
    ) => Promise<void>;
+
    editComment: (
+
      commentId: string,
+
      body: string,
+
      embeds: Embed[],
+
    ) => Promise<void>;
+
    reactOnComment: (
+
      publicKey: string,
+
      commentId: string,
+
      authors: Author[],
+
      reaction: string,
+
    ) => Promise<void>;
    repoDelegates: Author[];
-
    revisionId: string;
    rid: string;
  }

  /* eslint-disable prefer-const */
  let {
+
    cobId,
+
    commentThreads,
    config,
-
    discussion,
-
    patchId,
-
    reload,
+
    createComment,
+
    editComment,
+
    reactOnComment,
    repoDelegates,
-
    revisionId,
    rid,
  }: Props = $props();
  /* eslint-enable prefer-const */

  $effect(() => {
    // eslint-disable-next-line @typescript-eslint/no-unused-expressions
-
    patchId;
+
    cobId;

-
    hideDiscussion = discussion === undefined || discussion.length === 0;
+
    hideDiscussion = commentThreads.length === 0;
    focusReply = false;
    topLevelReplyOpen = false;
  });
@@ -54,106 +65,7 @@
  let focusReply: boolean = $state(false);
  let topLevelReplyOpen = $state(false);

-
  let hideDiscussion = $state(
-
    discussion === undefined || discussion.length === 0,
-
  );
-

-
  const threads = $derived(
-
    ((discussion &&
-
      discussion
-
        .filter(
-
          comment =>
-
            (comment.id !== revisionId && !comment.replyTo) ||
-
            comment.replyTo === revisionId,
-
        )
-
        .map(thread => {
-
          return {
-
            root: thread,
-
            replies:
-
              discussion &&
-
              discussion
-
                .filter(comment => comment.replyTo === thread.id)
-
                .sort((a, b) => a.edits[0].timestamp - b.edits[0].timestamp),
-
          };
-
        }, [])) as Thread[]) || [],
-
  );
-

-
  async function editComment(commentId: string, body: string, embeds: Embed[]) {
-
    try {
-
      await invoke("edit_patch", {
-
        rid: rid,
-
        cobId: patchId,
-
        action: {
-
          type: "revision.comment.edit",
-
          comment: commentId,
-
          body,
-
          revision: revisionId,
-
          embeds,
-
        },
-
        opts: { announce: $nodeRunning && $announce },
-
      });
-
    } catch (error) {
-
      console.error("Editing comment failed: ", error);
-
    } finally {
-
      await reload();
-
    }
-
  }
-

-
  async function createReply(replyTo: string, body: string, embeds: Embed[]) {
-
    try {
-
      await invoke("create_patch_comment", {
-
        rid: rid,
-
        new: { id: patchId, body, embeds, replyTo, revision: revisionId },
-
        opts: { announce: $nodeRunning && $announce },
-
      });
-
    } catch (error) {
-
      console.error("Creating reply failed", error);
-
    } finally {
-
      await reload();
-
    }
-
  }
-

-
  async function reactOnComment(
-
    publicKey: string,
-
    commentId: string,
-
    authors: Author[],
-
    reaction: string,
-
  ) {
-
    try {
-
      await invoke("edit_patch", {
-
        rid: rid,
-
        cobId: patchId,
-
        action: {
-
          type: "revision.comment.react",
-
          comment: commentId,
-
          reaction,
-
          revision: revisionId,
-
          active: !authors.find(
-
            ({ did }) => publicKeyFromDid(did) === publicKey,
-
          ),
-
        },
-
        opts: { announce: $nodeRunning && $announce },
-
      });
-
    } catch (error) {
-
      console.error("Editing comment reactions failed", error);
-
    } finally {
-
      await reload();
-
    }
-
  }
-

-
  async function createComment(body: string, embeds: Embed[]) {
-
    try {
-
      await invoke("create_patch_comment", {
-
        rid: rid,
-
        new: { id: patchId, body, embeds, revision: revisionId },
-
        opts: { announce: $nodeRunning && $announce },
-
      });
-
    } catch (error) {
-
      console.error("Creating comment failed: ", error);
-
    } finally {
-
      await reload();
-
    }
-
  }
+
  let hideDiscussion = $state(commentThreads.length === 0);

  async function toggleReply() {
    topLevelReplyOpen = !topLevelReplyOpen;
@@ -162,7 +74,7 @@
    }

    await tick();
-
    scrollIntoView(`reply-${patchId}`, {
+
    scrollIntoView(`reply-${cobId}`, {
      behavior: "smooth",
      block: "center",
    });
@@ -183,16 +95,20 @@
  }
</style>

-
<div style:margin={hideDiscussion ? "1.5rem 0" : "0 0 2.5rem 0"}>
+
<div style:margin={hideDiscussion ? "1.5rem 0" : "1.5rem 0 2.5rem 0"}>
  <div class="global-flex">
    <NakedButton
      variant="ghost"
-
      disabled={discussion === undefined || discussion.length === 0}
+
      disabled={commentThreads.length === 0}
      onclick={() => (hideDiscussion = !hideDiscussion)}>
      <Icon name={hideDiscussion ? "chevron-right" : "chevron-down"} />
      <div class="txt-semibold global-flex txt-regular">
        Discussion <span style:font-weight="var(--font-weight-regular)">
-
          {discussion?.length ?? 0}
+
          {sum(
+
            commentThreads.map(t => {
+
              return t.replies.length + 1;
+
            }),
+
          )}
        </span>
      </div>
    </NakedButton>
@@ -203,7 +119,7 @@
          if (hideDiscussion) {
            hideDiscussion = false;
          } else {
-
            if (discussion === undefined || discussion.length === 0) {
+
            if (commentThreads.length === 0) {
              hideDiscussion = true;
            }
          }
@@ -215,7 +131,7 @@
    </div>
  </div>
  <div class:hide={hideDiscussion} style:margin-top="1rem">
-
    {#each threads as thread}
+
    {#each commentThreads as thread}
      <ThreadComponent
        {thread}
        {rid}
@@ -224,13 +140,13 @@
          config.publicKey,
          repoDelegates.map(delegate => delegate.did),
        )}
-
        editComment={partial(editComment)}
-
        createReply={partial(createReply)}
+
        {editComment}
+
        createReply={createComment}
        reactOnComment={partial(reactOnComment, config.publicKey)} />
      <div class="connector"></div>
    {/each}

-
    <div id={`reply-${patchId}`}>
+
    <div id={`reply-${cobId}`}>
      <CommentToggleInput
        disallowEmptyBody
        {rid}
@@ -238,14 +154,14 @@
        onexpand={toggleReply}
        onclose={topLevelReplyOpen
          ? () => {
-
              if (discussion === undefined || discussion.length === 0) {
+
              if (commentThreads.length === 0) {
                hideDiscussion = !hideDiscussion;
              }
              topLevelReplyOpen = false;
            }
          : undefined}
        placeholder="Leave a comment"
-
        submit={partial(createComment)} />
+
        submit={createComment} />
    </div>
  </div>
</div>
modified src/components/Revision.svelte
@@ -6,6 +6,7 @@
  import type { Embed } from "@bindings/cob/thread/Embed";
  import type { PatchStatus } from "@app/views/repo/router";
  import type { Revision } from "@bindings/cob/patch/Revision";
+
  import type { Thread } from "@bindings/cob/thread/Thread";
  import type { Verdict } from "@bindings/cob/patch/Verdict";

  import partial from "lodash/partial";
@@ -66,6 +67,26 @@
    hideChanges = false;
  });

+
  const commentThreads = $derived(
+
    ((revision.discussion &&
+
      revision.discussion
+
        .filter(
+
          comment =>
+
            (comment.id !== revision.id && !comment.replyTo) ||
+
            comment.replyTo === revision.id,
+
        )
+
        .map(thread => {
+
          return {
+
            root: thread,
+
            replies:
+
              revision.discussion &&
+
              revision.discussion
+
                .filter(comment => comment.replyTo === thread.id)
+
                .sort((a, b) => a.edits[0].timestamp - b.edits[0].timestamp),
+
          };
+
        }, [])) as Thread[]) || [],
+
  );
+

  async function editRevision(
    revisionId: string,
    description: string,
@@ -139,6 +160,73 @@
    }
  }

+
  async function createComment(
+
    body: string,
+
    embeds: Embed[],
+
    replyTo?: string,
+
  ) {
+
    try {
+
      await invoke("create_patch_comment", {
+
        rid: rid,
+
        new: { id: patchId, body, embeds, replyTo, revision: revision.id },
+
        opts: { announce: $nodeRunning && $announce },
+
      });
+
    } catch (error) {
+
      console.error("Creating comment failed", error);
+
    } finally {
+
      await reload();
+
    }
+
  }
+

+
  async function editComment(commentId: string, body: string, embeds: Embed[]) {
+
    try {
+
      await invoke("edit_patch", {
+
        rid: rid,
+
        cobId: patchId,
+
        action: {
+
          type: "revision.comment.edit",
+
          comment: commentId,
+
          body,
+
          revision: revision.id,
+
          embeds,
+
        },
+
        opts: { announce: $nodeRunning && $announce },
+
      });
+
    } catch (error) {
+
      console.error("Editing comment failed: ", error);
+
    } finally {
+
      await reload();
+
    }
+
  }
+

+
  async function reactOnComment(
+
    publicKey: string,
+
    commentId: string,
+
    authors: Author[],
+
    reaction: string,
+
  ) {
+
    try {
+
      await invoke("edit_patch", {
+
        rid: rid,
+
        cobId: patchId,
+
        action: {
+
          type: "revision.comment.react",
+
          comment: commentId,
+
          reaction,
+
          revision: revision.id,
+
          active: !authors.find(
+
            ({ did }) => publicKeyFromDid(did) === publicKey,
+
          ),
+
        },
+
        opts: { announce: $nodeRunning && $announce },
+
      });
+
    } catch (error) {
+
      console.error("Editing comment reactions failed", error);
+
    } finally {
+
      await reload();
+
    }
+
  }
+

  async function loadHighlightedDiff(rid: string, base: string, head: string) {
    return invoke<Diff>("get_diff", {
      rid,
@@ -282,12 +370,13 @@
</div>

<Discussion
+
  cobId={patchId}
+
  {commentThreads}
  {config}
-
  discussion={revision.discussion}
-
  {patchId}
-
  {reload}
+
  {createComment}
+
  {editComment}
+
  {reactOnComment}
  {repoDelegates}
-
  revisionId={revision.id}
  {rid} />

<div
modified src/components/Thread.svelte
@@ -26,9 +26,9 @@
      embeds: Embed[],
    ) => Promise<void>;
    createReply?: (
-
      commentId: string,
      comment: string,
      embeds: Embed[],
+
      commentId: string,
    ) => Promise<void>;
    reactOnComment?: (
      commentId: string,
@@ -154,9 +154,9 @@
                try {
                  submitInProgress = true;
                  await createReply(
-
                    root.id,
                    comment,
                    Array.from(embeds.values()),
+
                    root.id,
                  );
                } finally {
                  showReplyForm = false;
modified src/views/repo/Issue.svelte
@@ -200,26 +200,6 @@
    }
  }

-
  async function createReply(replyTo: string, body: string, embeds: Embed[]) {
-
    try {
-
      await invoke("create_issue_comment", {
-
        rid: repo.rid,
-
        new: { id: issue.id, body, embeds, replyTo },
-
        opts: { announce: $nodeRunning && $announce },
-
      });
-
      // Update second column issue comment count without reloading the whole
-
      // issue list.
-
      const issueIndex = issues.findIndex(i => i.id === issue.id);
-
      if (issueIndex !== -1) {
-
        issues[issueIndex].commentCount += 1;
-
      }
-
    } catch (error) {
-
      console.error("Comment reply creation failed", error);
-
    } finally {
-
      await reload();
-
    }
-
  }
-

  async function editComment(id: string, body: string, embeds: Embed[]) {
    try {
      await invoke("edit_issue", {
@@ -574,7 +554,7 @@
              repo.delegates.map(delegate => delegate.did),
            )}
            {editComment}
-
            createReply={partial(createReply)}
+
            createReply={createComment}
            reactOnComment={partial(reactOnComment, config.publicKey)} />
          <div class="connector"></div>
        {/each}
@@ -589,7 +569,7 @@
              ? () => (topLevelReplyOpen = false)
              : undefined}
            placeholder="Leave a comment"
-
            submit={partial(createComment)} />
+
            submit={createComment} />
        </div>
      </div>
    </div>