Radish alpha
r
rad:z4D5UCArafTzTQpDZNQRuqswh3ury
Radicle desktop app
Radicle
Git
radicle-desktop src lib notification.ts
import type { ActionWithAuthor } from "@bindings/cob/inbox/ActionWithAuthor";
import type { Action as IssueAction } from "@bindings/cob/issue/Action";
import type { Action as PatchAction } from "@bindings/cob/patch/Action";

import { formatOid, pluralize } from "@app/lib/utils";

export type Action =
  | ActionWithAuthor<IssueAction>
  | ActionWithAuthor<PatchAction>;

// N.b. I have taken the `%` char as indicator for a `txt-id` class
export function createSummary(
  a: Action[],
  kind: "issue" | "patch",
  oid: string,
  count: number,
) {
  const lastAction = a[a.length - 1];
  let summary = `${lastAction.type} not implemented!`;

  function times(count: number) {
    return count > 1 ? `${count} times` : "";
  }

  if (lastAction.oid === oid) {
    summary = `opened ${kind} %${formatOid(lastAction.oid)}%`;
  } else if (lastAction.type === "comment") {
    summary = `left ${count > 1 ? count : "a"} ${pluralize("comment", count)}`;
  } else if (lastAction.type === "revision") {
    const revisions = a.map(i => `%${formatOid(i.oid)}%`).slice(0, 10);
    summary = `created ${pluralize("revision", count)} ${[
      ...revisions,
      ...(a.length >= 11 ? ["%…%"] : []),
    ].join(", ")}`;
  } else if (lastAction.type === "merge") {
    summary = `merged ${pluralize("revision", count)} %${formatOid(lastAction.revision)}%`;
  } else if (lastAction.type === "edit" && kind === "issue") {
    summary = `edited issue${count ? times(count) : ""}`;
  } else if (lastAction.type === "edit" && kind === "patch") {
    summary = `edited ${pluralize("revision", count)} %${formatOid(lastAction.oid)}%`;
  } else if (lastAction.type === "revision.edit") {
    summary = `edited ${pluralize("revision", count)} %${formatOid(lastAction.revision)}%`;
  } else if (lastAction.type === "lifecycle" && count > 1) {
    summary = `changed to ${lastAction.state.status} and ${count} more changes`;
  } else if (
    lastAction.type === "lifecycle" &&
    lastAction.state.status === "draft"
  ) {
    summary = `changed to ${lastAction.state.status}`;
  } else if (
    lastAction.type === "lifecycle" &&
    lastAction.state.status === "open"
  ) {
    summary = `reopened ${kind}`;
  } else if (
    lastAction.type === "lifecycle" &&
    lastAction.state.status === "archived"
  ) {
    summary = `archived ${kind}`;
  } else if (
    lastAction.type === "lifecycle" &&
    lastAction.state.status === "closed" &&
    lastAction.state.reason === "solved"
  ) {
    summary = `closed ${kind} as solved`;
  } else if (
    lastAction.type === "lifecycle" &&
    lastAction.state.status === "closed"
  ) {
    summary = `closed ${kind}`;
  } else if (lastAction.type === "revision.comment") {
    summary = `left ${count > 1 ? count : "a"} ${pluralize("comment", count)}`;
  } else if (lastAction.type === "review.comment") {
    summary = `left ${count > 1 ? count : "a"} review ${pluralize("comment", count)}`;
  } else if (a.every(e => e.type === "comment.react")) {
    const reactions = a.map(i => i.reaction).slice(0, 10);
    summary = `reacted with ${[
      ...reactions,
      ...(a.length >= 11 ? ["%…%"] : []),
    ].join(", ")} to ${count > 1 ? count : "a"} ${pluralize("comment", count)}`;
  } else if (lastAction.type === "comment.edit") {
    summary = `edited ${count > 1 ? count : "a"} ${pluralize("comment", count)}`;
  } else if (lastAction.type === "review" && lastAction.verdict) {
    summary = `${lastAction.verdict}ed revision %${formatOid(lastAction.revision)}% with a review`;
  } else if (lastAction.type === "review") {
    summary = `left a review with a comment on revision %${formatOid(lastAction.revision)}%`;
  } else if (lastAction.type === "assign") {
    summary = "changed assigns";
  } else if (lastAction.type === "revision.comment.edit") {
    summary = `edited ${count > 1 ? count : "a"} ${pluralize("comment", count)}`;
  } else if (lastAction.type === "comment.redact") {
    summary = `redacted ${count > 1 ? count : "a"} ${pluralize("comment", count)}`;
  } else if (lastAction.type === "label") {
    summary = `added ${pluralize("label", count)}`;
  } else if (lastAction.type === "review.comment.edit") {
    summary = `edited ${count > 1 ? count : "a"} review ${pluralize("comment", count)}`;
  } else if (lastAction.type === "review.comment.react") {
    summary = `reacted to ${count > 1 ? count : "a"} review ${pluralize("comment", count)}`;
  } else if (lastAction.type === "review.comment.redact") {
    summary = `redacted ${count > 1 ? count : "a"} review ${pluralize("comment", count)}`;
  } else if (lastAction.type === "review.comment.resolve") {
    summary = `resolved ${count > 1 ? count : "a"} review ${pluralize("comment", count)}`;
  } else if (lastAction.type === "review.comment.unresolve") {
    summary = `unresolved ${count > 1 ? count : "a"} review ${pluralize("comment", count)}`;
  } else if (lastAction.type === "review.edit") {
    summary = `edited review ${times(count)}`;
  } else if (lastAction.type === "review.redact") {
    summary = `redacted ${count > 1 ? count : "a"} ${pluralize("review", count)}`;
  } else if (lastAction.type === "revision.comment.react") {
    summary = `reacted to ${count > 1 ? count : "a"} revision ${pluralize("comment", count)}`;
  } else if (lastAction.type === "revision.comment.redact") {
    summary = `redacted ${count > 1 ? count : "a"} revision ${pluralize("comment", count)}`;
  } else if (lastAction.type === "revision.react") {
    summary = `reacted to ${count > 1 ? count : "a"} ${pluralize("revision", count)}`;
  } else if (lastAction.type === "revision.redact") {
    summary = `redacted ${count > 1 ? count : "a"} ${pluralize("revision", count)}`;
  }

  return summary.replaceAll(/[%](\S+)[%]/g, '<span class="txt-id">$1</span>');
}

export function compressActions(
  actions: Action[],
  kind: "issue" | "patch",
  oid: string,
) {
  const result: {
    summary: string;
    oid: string;
    items: Action[];
  }[] = [];

  let currentGroup: Action[] = [];

  for (const action of actions) {
    if (currentGroup.length === 0) {
      currentGroup.push(action);
      continue;
    }

    const last = currentGroup[currentGroup.length - 1];
    const sameAuthorDid = last.author.did === action.author.did;
    const sameType =
      last.type === action.type ||
      (last.type === "revision.edit" && action.type === "edit") ||
      (action.type === "revision.edit" && last.type === "edit") ||
      action.oid === oid;

    if (sameAuthorDid && sameType) {
      currentGroup.push(action);
    } else {
      const summaryStr = createSummary(
        currentGroup,
        kind,
        oid,
        currentGroup.length,
      );

      result.push({
        summary: summaryStr,
        oid,
        items: currentGroup,
      });

      currentGroup = [action];
    }
  }

  // Summarize any open actions.
  if (currentGroup.length > 0) {
    const summaryStr = createSummary(
      currentGroup,
      kind,
      oid,
      currentGroup.length,
    );
    result.push({
      summary: summaryStr,
      oid,
      items: currentGroup,
    });
  }

  return result;
}